838 Commits

Author SHA1 Message Date
chardub
13c7792686 dna splicers that don't crash the game... 2025-05-09 20:30:35 -04:00
chardub
4ecf97b777 DNA Splicers WIP 2025-05-08 22:59:30 -04:00
chardub
6536fcda77 More characters customization 2025-04-27 12:17:35 -04:00
chardub
4a19fbc754 Outfits migration again 2025-04-27 00:37:26 -04:00
chardub
c0cf7da7bb Overworld Outfits 2025-04-27 00:14:10 -04:00
chardub
bbd5ba09ea More migration 2025-04-26 22:03:51 -04:00
chardub
90c0e51e88 Caches expanded autotiles to reduce stutter caused by map connections 2025-04-26 22:01:12 -04:00
chardub
85d5f6206c custom autotile replacements 2025-04-26 21:35:28 -04:00
chardub
b412ad7b39 Migration - more progress 2025-04-25 22:06:46 -04:00
chardub
7de024dafd Renames $Trainer to $player 2025-04-24 19:57:12 -04:00
chardub
95ef337de8 Migrate a reorganizes bunch of files from PIF 2025-04-24 19:46:52 -04:00
chardub
682323e255 feature(Implements speed up) 2025-04-19 10:59:33 -04:00
Maruno17
8d0061bf3d Bug fixes 2025-02-28 21:55:11 +00:00
Maruno17
39a11e8ea8 Refactoring of new UI 2025-01-23 23:47:20 +00:00
Maruno17
6f37cb7e33 Moved battle music filenames to Settings, improved PluginManager's version comparer, removed player bump animation, fixed No Guard not applying to OHKO moves, can reuse the same Repel item quicker 2025-01-22 23:02:34 +00:00
Maruno17
db2df5c8b4 Combined overwriting code from the new animation editor files 2025-01-17 00:18:27 +00:00
Maruno17
0c0c826e82 Merge branch 'animations' into dev 2025-01-16 22:56:15 +00:00
Maruno17
bfadd7ff9f C key no longer toggles Animation Editor's colour theme 2025-01-16 22:55:45 +00:00
Maruno17
e81fe77bcc Merge branch 'ui-redesign' into dev 2025-01-16 22:54:12 +00:00
Maruno17
d03f012162 Rewrote Town Map screen 2025-01-16 00:09:59 +00:00
Maruno17
77772bab6d Fixed Fluctuating growth rate's equation for levels above 100 2024-11-23 14:59:42 +00:00
Maruno17
fcb4a1cec3 Merge branch 'dev' into ui-redesign 2024-11-22 19:55:58 +00:00
Maruno17
00d1e431b4 Some bug fixes, added PriorityChange item handler 2024-11-22 00:38:20 +00:00
Maruno17
098ca562d0 Rewrote Move Reminder screen 2024-11-03 01:15:53 +00:00
Maruno17
ae80d9dcd4 Changed choice lists to allow text formatting 2024-11-01 23:56:04 +00:00
Maruno17
210cfc654f Added Setting SHOW_MODIFIED_MOVE_PROPERTIES, other minor tweaks and fixes 2024-10-29 22:14:36 +00:00
Maruno17
35be8fcf67 Added support for all three Gen 4 location sign styles, minor tweaks to other code 2024-10-28 23:18:57 +00:00
Maruno17
3eb2724794 Rewrote save game screen, added rest of multiple save file support 2024-10-22 22:17:19 +01:00
Maruno17
6152b75cb1 Rewrote load game screen inc. supporting multiple save files, allowed \PN and \v[42] in map names and Town Map point names 2024-10-20 23:46:56 +01:00
Maruno17
89c344dc00 Rewrote Pokémon storage screen code 2024-10-13 23:41:42 +01:00
Maruno17
61b6bb5aeb Rewrote party screen debug code, misc code changes 2024-10-01 19:10:34 +01:00
Maruno17
fc538a09f7 Fixed Focus Band only triggering at full HP, made more text translatable 2024-09-30 21:44:36 +01:00
Maruno17
b80de83b0d Rewrote BP Shop code, tidied PC menu code, fixed message display bug involving instant speed and \wtnp, fixed mail vanishing bug, tweaks to other rewritten UI 2024-09-27 22:10:26 +01:00
Maruno17
48292c2a28 Increased max mone amount, rewrote Mart code, redesigned Mart and selling items a little 2024-09-26 01:00:18 +01:00
Maruno17
d1b0f5bce8 Rewrote item descriptions that are now too long for the space provided 2024-09-21 23:55:05 +01:00
Maruno17
a69e6bc6b9 Added Mail and MegaStones pseudo-pockets, deleted contest scarf items 2024-09-21 19:30:54 +01:00
Maruno17
6d1013d34e Rearranged Bag pockets, replaced Mail pocket with Held Items pocket 2024-09-21 19:10:32 +01:00
Maruno17
f62a357cbd Merge branch 'dev' into ui-redesign 2024-09-20 21:30:46 +01:00
Maruno17
012814f557 Plugins now recompile if any plugin name/version doesn't match one that was previously compiled, fixed Throat Chop's effect 2024-09-20 21:30:10 +01:00
Maruno17
2c071b224f Added more stats, added script variables, fixed AI thinking Wonder Guard provides total immunity, releasing a Pokémon puts its held item in the Bag, tweaked new map Compiler 2024-09-20 00:51:54 +01:00
Maruno17
44c4a50a1a Generalised UI slider-drawing code 2024-09-19 23:08:25 +01:00
Maruno17
2816cbcd92 More Bag UI redesign implementation 2024-09-18 23:55:50 +01:00
Maruno17
4770057818 Added parent pocket functionality to BagPocket, started implementing Bag UI redesign 2024-09-17 00:20:41 +01:00
Maruno17
801c2d35c6 Implemented GameData BagPocket 2024-09-14 01:27:17 +01:00
Maruno17
d8263da05e Rewrote Bag screen 2024-09-13 23:01:40 +01:00
Maruno17
9c95db2324 Tidying up 2024-09-09 21:31:22 +01:00
Maruno17
2190f7c251 Rewrote party screen, implemented redesign of it 2024-09-07 21:51:54 +01:00
Maruno17
fab5fc0641 New UI no longer activates as part of initializing 2024-08-30 21:54:12 +01:00
Maruno17
89e6df6e06 Rewrote summary screen cursors, added cropping of relevant text in the summary screen, raised max lengths of player and Pokémon names to 12 2024-08-30 19:34:58 +01:00
Maruno17
9a7dfbb587 More refactoring of summary screen code, added module UIActionHandlers 2024-08-28 21:19:55 +01:00
Maruno17
2abdf333db Implemented redesign of summary screen 2024-08-27 00:49:11 +01:00
Maruno17
273e1bb565 Refactored summary screen code 2024-08-24 22:14:29 +01:00
Maruno17
45127be5b6 Rewrote summary screen code 2024-08-24 17:00:06 +01:00
Maruno17
2c6fe70f0c Fixed and implemented new pause menu and Trainer Card scripts 2024-08-22 22:26:57 +01:00
Maruno17
f4358e1542 Wrote preliminary UI base classes, wrote trainer card and pause menu screens using them 2024-08-22 00:20:23 +01:00
Maruno17
8841a534fe Some more code for Gen 9 abilities 2024-08-21 22:11:01 +01:00
Maruno17
6cc07d1c7a Removed deprecated methods 2024-06-27 21:37:00 +01:00
Maruno17
509a414f37 More or less standardised separator comments in the code 2024-06-27 21:21:26 +01:00
Maruno17
225549bfce Added consts for icon sizes for status and category 2024-06-26 16:26:53 +01:00
Maruno17
03b0fa100a Fixed some untranslatable messages, updated text files 2024-06-25 20:40:59 +01:00
Maruno17
195c34a7c8 Added SKIP_TITLE_SCREEN Setting, added const for type icon size, fixed some battle bugs and inconsistencies 2024-06-25 15:55:10 +01:00
Maruno17
ff2d8e5f55 More Gen 9 move effects 2024-06-24 21:51:49 +01:00
Maruno17
8e9417c3b7 Snowstorm, forfeiting trainer battles, battle outcome values 2024-06-15 21:29:00 +01:00
Maruno17
22b33ca6c2 Coded some Gen 9 ability/item/move effects 2024-06-15 15:58:31 +01:00
Maruno17
454d5a216a Updated Gen 9 descriptions to make them fit 2024-06-11 20:02:26 +01:00
Maruno17
5e23984765 Added Gen 9 forms code, evolution code, some item effects 2024-06-08 19:35:45 +01:00
Maruno17
6c3d9e77ad Added Gen 9 PBS files 2024-06-08 15:41:34 +01:00
Maruno17
b86d5b5b57 Fixed incorrect reflection layering, fixed Metal Burst counting damage taken by a substitute, fixed Metal Burst not trying to target all battlers that hit it, fixed Mega Scizor's stats 2024-05-16 19:18:35 +01:00
Maruno17
209a1ff1b3 Ensured messages are recompiled if PBS files or map data is compiled 2024-05-14 22:25:25 +01:00
Maruno17
01c13ada76 Made Compiler more modular 2024-05-14 22:11:12 +01:00
Maruno17
5bef70fb3a Moves fail because of semi-invulnerability instead of other immunities, fixed Pokédex not registering Pokémon in Safari battles, other things 2024-05-14 20:37:32 +01:00
Maruno17
63309a2ae9 Anim Editor: added dark colour scheme 2024-05-13 20:32:20 +01:00
Maruno17
5495bf565c Anim Editor: Added smart angle property to particles 2024-05-11 00:33:56 +01:00
Maruno17
34741ea840 Anim Editor: made the window height depend on the monitor's height 2024-05-06 00:46:02 +01:00
Maruno17
8aacfe491f Anim Editor: Particle spawner tweaks 2024-05-04 22:20:05 +01:00
Maruno17
dba28332f2 Anim Editor: added basic particle spawner functionality and graphic frame randomiser 2024-05-04 19:17:23 +01:00
Maruno17
aef67341d2 Minor tweaks and fixes 2024-04-27 00:18:16 +01:00
Maruno17
04985eab5c Added "airborne" event name tag, can have Safari battles with no party, fixed Micle Berry, fixed data box not refreshing when Illusion is broken, added triggers for more EoR animations 2024-04-27 00:10:28 +01:00
Maruno17
81ce6e515c More new battle animations 2024-04-24 22:49:19 +01:00
Maruno17
99aec45c5c Anim Editor: bug fixes relating to deleting particles, added some animations 2024-04-22 23:50:54 +01:00
Maruno17
23a8c552d6 Anim Editor: bug fixes 2024-04-21 23:08:48 +01:00
Maruno17
032ad25adc Anim Editor: z-related bug fix, full-screen graphic size is now more lenient 2024-04-21 19:13:54 +01:00
Maruno17
53eff70d63 Anim Editor: added changing of editor settings 2024-04-21 00:53:01 +01:00
Maruno17
a80dd5adb2 Anim Editor: added Insert/Delete keyboard shortcuts 2024-04-20 18:29:48 +01:00
Maruno17
4480def33c Anim Editor: added FoeFlip property, Space to play, S to swap sides, P to show/hide property lines for selected particle 2024-04-18 22:35:15 +01:00
Maruno17
15033d6114 Anim Editor: improved NumberTextBox entry, added "FoeInvertX/Y" particle properties, tidied up 2024-04-15 22:42:46 +01:00
Maruno17
a548a1ae9d Anim Editor: Fixes 2024-04-13 22:38:23 +01:00
Maruno17
184ce47b93 Anim Editor: colour changes 2024-04-13 22:13:21 +01:00
Maruno17
c14faf3fed Anim Editor: Changes to example animations and converter 2024-04-13 22:12:11 +01:00
Maruno17
44cc500fdc Anim Editor: added play functionality to battle and editor 2024-04-13 16:28:52 +01:00
Maruno17
d0e15a8939 Anim Editor: List control no longer fills in its background 2024-04-07 23:49:23 +01:00
Maruno17
f34f9040c6 Anim Editor: graphics and audio now support subfolders, other tweaks 2024-04-07 19:51:17 +01:00
Maruno17
29140a517e List control now draws its own background 2024-04-04 22:39:01 +01:00
Maruno17
9c3314843a Anim Editor: refactoring side pane code 2024-04-04 21:33:08 +01:00
Maruno17
494e646fd5 Fixed replacement battler being invisible if its predecessor fainted and used the same sprite, refactoring 2024-04-01 23:41:54 +01:00
Maruno17
a2af2c36f9 Added water ripple animation, removed second error message when the Compiler crashes 2024-04-01 22:13:16 +01:00
Maruno17
1977bd866c Anim Editor: added filter text box to selection screen, disabled animations are listed in red 2024-03-31 23:19:31 +01:00
Maruno17
76e2b5a4fb Anim Editor: added particle frames to canvas, added mouse interactions to canvas 2024-03-31 00:12:51 +00:00
Maruno17
323b62b7d5 Anim Editor: polishing, refactoring, ensuring data 2024-03-25 22:08:11 +00:00
Maruno17
8a218ca834 Anim Editor: added color and tone side pane 2024-03-25 16:44:43 +00:00
Maruno17
054d7820e4 Anim Editor: added interpolation editing 2024-03-16 00:08:00 +00:00
Maruno17
ae32d59eb9 Anim Editor: added more animation interpolation types, greyed out timeline that isn't part of the animation 2024-03-12 19:11:51 +00:00
Maruno17
1ff5b12acd Added "beneath map" height for overworld animations, fixed new games inheriting Jukebox BGM from save file, added PokeBall property for trainer types, fixed Cramorant form changing effects 2024-03-11 23:31:48 +00:00
Maruno17
f0fae4b9ec Anim Editor: added NoUser property, added buttons to duplicate/delete particle and delete single commands 2024-02-29 00:54:01 +00:00
Maruno17
47be44a54c Renamed townmapgen, embedded its graphics in the html file 2024-02-28 20:23:29 +00:00
Maruno17
1c9ce0b01a Fixed Event Touch events on connected maps triggering themselves by moving around 2024-02-20 19:33:13 +00:00
Maruno17
24dda5128a Improved fix of phone data conversion 2024-02-15 22:13:10 +00:00
Maruno17
3e167b9357 Fixed type-resisting berries trying to be consumed twice when triggered by a move called by another move, fixed bad old phone data conversion, fixed events of even sizes moving endlessly when approaching the player 2024-02-15 21:16:15 +00:00
Maruno17
67acf46859 Added new/shift buttons to Anim Editor timeline 2024-02-15 20:15:03 +00:00
Maruno17
86c92e7657 Fixed reflections not disappearing if the parent event changes to a blank page 2024-02-02 00:07:32 +00:00
Maruno17
e6fe242d4b AI changes, fixed input detection Conditional Branch, Fishious Rend is now a biting move 2024-02-01 23:50:09 +00:00
Maruno17
4455c093b8 Added canvas to new animation editor (isn't interactive yet), improved example animations 2024-01-25 21:07:16 +00:00
Maruno17
c78e32db09 Fixed Database animations' position not moving the animation for exclamations 2024-01-18 23:47:33 +00:00
Maruno17
97a66020ca Fixed being able to jump over a ledge the wrong way if it's on the edge of a connected map 2024-01-18 22:40:58 +00:00
Maruno17
c5d7d1447b Added Setting for item sell prices, added stat for primal reversions, fixed weird movement when jumping across a map connection 2024-01-18 22:20:46 +00:00
Maruno17
94f0a9c8d0 Made New/Copy/Delete buttons in animation selector screen work, split animations compiler into its own compiler 2024-01-02 23:34:59 +00:00
Maruno17
732e09336a Merge branch 'master' into animations 2024-01-02 16:01:28 +00:00
Maruno17
8c5911e4a4 Fixed being able to bypass a caught mon being forced into the party, fixed Rotom Catalog, fixed incorrect writing of some enums to PBS files, fixed Jukebox's awareness of audio files, fixed bug when battle default weather is primordial, disabled path cache to add speed 2024-01-01 20:35:28 +00:00
Maruno17
6bd35d44c4 Added TextBoxDropdownList UI control 2024-01-01 18:12:53 +00:00
Maruno17
d5c7b8cc15 Updated core translation text files 2023-12-12 20:32:16 +00:00
Maruno17
2f231a25bb Tidied up TODO comments, misc tweaks to Anim Editor 2023-12-03 23:47:38 +00:00
Maruno17
b4e7b765d1 Added the animation properties pop-up window 2023-12-02 01:39:43 +00:00
Maruno17
b69f1fc5a6 Fleshed out Animation Editor's chooser screen, added lots of example animation PBS files 2023-11-30 22:16:42 +00:00
Maruno17
5553218507 UIControls can be disabled, added blacklist to TextBox control 2023-11-29 23:39:10 +00:00
Maruno17
973b93a524 Added gra;hic/SE chooser pop-up windows to Animation Editor 2023-11-28 22:28:08 +00:00
Maruno17
01ff59606b Merge branch 'dev' into animations 2023-11-27 18:27:38 +00:00
Maruno17
c7e8848813 Added min. 3 perfect IVS for legendaries, rerolling IVs for Safari/Bug Contest Pokémon, fixed Big Nugget's Fling power in Gen 8 2023-11-27 18:26:07 +00:00
Maruno17
d3c9c6b360 External script loader now ignores non-.rb files and folders beginning with ".", updated extractor/combiner scripts 2023-11-27 18:01:04 +00:00
Maruno17
1080f69a0d Fixed location signpost not appearing properly when using Fly, fixed wild/trainer Pokémon with a "getForm" handler not using it 2023-11-20 22:11:32 +00:00
Maruno17
e0dcdef321 Fixed partner trainers not having Bag items, fixed Flame Burst AI bug, added AI for Wonder Guard + switching 2023-11-20 21:29:47 +00:00
Maruno17
b54a96f23f Add menu bar to Animation Editor, some refactoring 2023-11-19 22:13:47 +00:00
Maruno17
d10892af47 Fixed bug in previous commit relating to writing PBS files 2023-11-06 20:02:32 +00:00
Maruno17
f07b44d826 Minor lag improvements to overworld animations 2023-11-05 16:15:41 +00:00
Maruno17
156a6fca74 Fixed BGM bug when ending surfing and immediately triggering a trainer battle with an intro BGM, fixed events making all other events check if they trigger after the event moves, fixed Neutralizing Gas triggering twice upon fainting 2023-11-05 15:42:15 +00:00
Maruno17
092fbda34d Fixed being unable to write values to PBS files that were enumerated to something other than a number, error log files now go in the game's folder 2023-11-04 23:32:43 +00:00
Maruno17
ab2d2c1356 Added header variant of Label control, makde DropdownList control 2023-11-04 23:12:25 +00:00
Maruno17
64890f3c9e Rearranged and renamed Animation Editor-related script files 2023-10-23 23:44:34 +01:00
Maruno17
340983e765 Fleshing out animation editor's code 2023-10-23 22:36:43 +01:00
Maruno17
7031698d85 Added animation editor's particle list 2023-10-18 16:48:28 +01:00
Maruno17
5cab0f407d Split "Evolutions" line in PBS files into multiple "Evolution" lines 2023-10-14 19:15:38 +01:00
Maruno17
389d43941d Added SKIP_CONTINUE_SCREEN Setting, fix message newline visual bug at slow text speeds, removed Bag rearranging 2023-10-14 18:32:18 +01:00
Maruno17
25f85a9a8b Added mp3 support back in, changed layout of townmapgen.html, screenshots now go in Screenshots folder, added "NoName" flag for trainer types 2023-10-14 16:28:37 +01:00
Maruno17
193f01f70b Refactored scrollbar into its own control 2023-10-06 20:59:30 +01:00
Maruno17
79ffcd3230 Animation editor now uses proper animation data, misc other code tweaks to animation editor 2023-10-01 20:39:04 +01:00
Maruno17
d267956c6e Fixed being able to Fly in the Town Map despite the Setting, fixed being unable to interact with an event next to you if standing on an event 2023-10-01 19:24:49 +01:00
Maruno17
a6c7e2c1ff Added Setting that prompts compiling upon startup 2023-10-01 18:52:48 +01:00
Maruno17
cd32b5e725 Fixed language files not being loadable in an encrypted game, fixed language files not reverting to default if they don't exist and other language files are already loaded 2023-10-01 18:06:54 +01:00
Maruno17
e96f16c484 Tweaked Compiler error messages, made some in def cast_csv_value show if trying to cast nil 2023-10-01 17:51:13 +01:00
Maruno17
2ff47cf40d Created animation PBS file compiler and writer 2023-09-24 18:20:32 +01:00
Maruno17
d9c3898124 Fixed long battle messages displaying weirdly 2023-09-23 19:20:44 +01:00
Maruno17
b2c66b7b0c Fixed typo relating to AI switching, fixed Pokémon sent from the party to storage in battle not resetting their battle-only conditions, fixed player's sprite in Duel minigame not caring about the player's outfit, added missing move flags 2023-09-21 19:36:10 +01:00
Maruno17
bc18aa95f2 Fixed Cramorant not reverting form after coughing up a Gulp Missle, fixed crash when a phone contact calls when you're on a map with no metadata 2023-09-10 16:57:09 +01:00
Maruno17
8f00307685 Made Voltorb Flip board generation more accurate to HGSS 2023-09-10 16:26:05 +01:00
Maruno17
02e45ebf19 NamedEvents can now be overwritten, fixed error in validating all types, replaced wiki shortcut 2023-09-10 15:59:14 +01:00
Maruno17
d4077875a4 Implemented list control and basic animation-choosing screen for editor 2023-08-31 23:03:47 +01:00
Maruno17
1041883992 Initial proof of concept commit 2023-08-28 22:41:48 +01:00
Maruno17
efea53aa5a Fixed Pokédex showing genders for the wrong species, fixed AI always switching sleeping/frozen Pokémon, fixed class PngAnimatedBitmap animating slowly 2023-08-23 21:08:32 +01:00
Maruno17
ea7b5d56d2 Fixed crash upon soft resetting 2023-07-31 23:40:02 +01:00
Maruno17
bd70166dbb Fixed follower teleporting behind the player when they bump into something to the side 2023-07-31 20:58:57 +01:00
Maruno17
3d4a495849 Tidying up for v21.2 release 2023-07-30 21:43:27 +01:00
Maruno17
4c25ade184 Fixed overworld weather fading out/in when walking between maps with the same weather, added support for Sprite coordinates being floats 2023-07-30 20:32:39 +01:00
Maruno17
4f14108772 Minor tidying and updating 2023-07-29 20:20:08 +01:00
Maruno17
1b5c0f6f2f Updated mkxp-z 2023-07-29 20:17:06 +01:00
Maruno17
9a42b533f1 Added more sound effects 2023-07-29 20:08:31 +01:00
Maruno17
1c860a5544 Renamed PBS backup folders for clarity 2023-07-24 22:17:26 +01:00
Maruno17
0068695c54 Fixed incorrect AI code for considering Sticky Web when switching 2023-07-22 18:43:28 +01:00
Maruno17
fc95baf92c Now loads language files on startup if Settings::LANGUAGES has exactly 1 defined language, fixed instant text speed after a wait in a message, added battle rule "cannotSwitch" 2023-07-20 22:05:50 +01:00
Maruno17
a5734eaf46 Code tidying with Rubocop 2023-07-18 22:42:10 +01:00
Maruno17
6053363715 Added Settings::DISABLE_IVS_AND_EVS, added text replacements for gender symbols 2023-07-17 19:28:05 +01:00
Maruno17
f576db7c0b Fixed the first frame of RMXP Database animations not showing 2023-07-15 20:08:36 +01:00
Maruno17
4ca2500d6b The Pokédex entry of a newly obtained species now only shows if that species is in an unlocked Dex list 2023-07-12 21:08:02 +01:00
Maruno17
8287f32a5e Fixed another crash when entering a map with no map metadata, fixed Sky Drop failing causing the target to remain in the air, decapitalised some Debug menu text 2023-07-12 21:06:54 +01:00
Maruno17
104bf2b598 Added some graphics filenames to the core messages 2023-07-01 22:49:02 +01:00
Maruno17
2986ab3ebb Rewrote mkxp.json, fix crash when entering a map with no map metadata, fixed Battle Factory menu text misalignment 2023-07-01 22:11:02 +01:00
Maruno17
2078f6b116 Ensured consistent PBS file layouts, fixed some script file numberings 2023-06-27 20:38:15 +01:00
Maruno17
60f8a0cf7b Fixed potential slight irregularities in effect chances of elemental Fang moves, tweaked how credits are gathered, 2023-06-27 19:16:14 +01:00
Maruno17
aecd9e5bb6 Tidying up for v21's release 2023-06-25 22:31:05 +01:00
Maruno17
22fa0f9c0b Added messages_core.dat and extracted text from it to GitHub, moved game credits to Settings, made credits translatable, tweaked some messages 2023-06-25 16:53:13 +01:00
Maruno17
7d77c5f3fc Tweaked some overworld weather animations, added NPCTrainer#version, added Debug function for editing more things in the phone and its contacts 2023-06-24 23:52:09 +01:00
Maruno17
4fc0806c8a Ensured random dungeons place large events properly 2023-06-20 19:26:40 +01:00
Maruno17
17e8be9dca Fixed error in Powder's backfire message, fixed move disruption affecting recharge moves when it shouldn't, fixed error when Shell Side Arm has no targets, fixed AI not unregistering Mega Evolution if it won't do so after all 2023-06-19 23:55:49 +01:00
Maruno17
539bc0fb50 More AI bug fixes, more work on testing AI 2023-06-18 23:36:06 +01:00
Maruno17
b5e37248b9 Pokémon sent into battle now default to the Fight option, fixed bugs in Instruct, fixed some AI bugs, fixed parameter mixup for def pbMoveCanTarget?, renamed function to function_code everywhere, fixed black party Pokémon icons in storage, added some more AI testing code 2023-06-18 20:12:36 +01:00
Maruno17
9c2a9130a5 Consolidated code for the player interacting in the overworld, fixed bad splash screen animations, added ShowQuantity property to items.txt, fixed Quick Draw and Aroma Veil, fixed text alignment in long list of regional Dexes in Pokédex, added better error message in load screen if player's charset is missing 2023-06-12 22:32:41 +01:00
ENLS
20affb4345 Update 004_Game_Map.rb (#210) 2023-06-06 20:44:08 +01:00
Maruno17
679e9d42dc Moved Poké Radar encounters into encounters.txt, added Setting/map metadata for reflection rippling, fixed def minimum_level, fixed ice/waterfalls forcing movement even when holding Ctrl in Debug mode, Pokédex no longer shows genders for species without gender differences 2023-06-04 20:40:42 +01:00
Maruno17
3470f9769c Synced FPS to monitor's refresh rate, fixed broken waterfall movement 2023-06-03 22:33:49 +01:00
Maruno17
1901675e33 Finished FPS agnosticism, removed particle engine 2023-06-03 21:55:02 +01:00
Maruno17
68de25562a Removed last possible usages of Graphics.frame_rate and Graphics.frame_count, improved screen scrolling code, player now animates walking into a wall 2023-05-28 17:25:30 +01:00
Maruno17
f27841a7f8 Better version of the previous commit's fix 2023-05-25 15:38:29 +01:00
Maruno17
01a6869061 Fixed freeze from previous commit relating to script-type move route commands 2023-05-25 15:24:14 +01:00
Maruno17
c756e2647a More FPS agnosticism, fixed pause after finishing an event's repeating move route 2023-05-24 21:20:20 +01:00
Maruno17
167155c67d Deprecated methods intended to work around filenames with accents, fixed crash when the Compiler wants to rewrite PBS files if they don't exist 2023-05-20 22:10:11 +01:00
Maruno17
276c052822 Updated mkxp-z version, fixed BGM issues caused by v2.4.2 2023-05-20 21:03:08 +01:00
Maruno17
d112e2361a A lot of FPS agnosticism, added def lerp 2023-05-20 18:37:54 +01:00
Maruno17
62e372f4d7 Improved usage of Time.now and pbGetTimeNow 2023-05-17 19:24:38 +01:00
Maruno17
28a2b7c9c1 Removed Graphics.delta_s and the usage thereof, tweaked credits screen code 2023-05-17 18:50:38 +01:00
Maruno17
a96867d537 Updated to mkxp-z v2.4.2 2023-05-15 20:00:56 +01:00
Maruno17
e7e153bf11 Fixed Rollout being affected by Parental Bond, fixed multi-turn moves being stopped by Torment, fixed BGM not stopping when returning to the title screen, removed AI references 2023-05-15 19:41:34 +01:00
Maruno17
6ca7da0a06 Merge branch 'ai' into dev 2023-05-14 18:36:25 +01:00
Maruno17
a4d74a9663 Resolved all remaining TODO comments for AI (except testing), fixed effects of moves that can end the battle 2023-05-13 22:49:09 +01:00
Maruno17
7a8754c425 Waged war against TODO comments in the AI, some refactoring of AI 2023-05-07 23:12:39 +01:00
Maruno17
5a2f0723ab Fixed broken file paths for BP shop 2023-05-06 22:45:50 +01:00
Maruno17
a397e60ca5 Added Debug menu functions, rearranged Debug menu more, fixed Pokémon sprite not refreshing when changing its held item in its summary screen 2023-05-06 18:25:56 +01:00
Maruno17
72469bbf79 Added Debug function to edit Repel steps, Flash/Strength usage and Black/White Flute effects; split old and new Black/White Flute effects 2023-05-05 20:17:07 +01:00
Maruno17
48fb8dae73 Removed redundant "\r" from various messages, removed usages of BitmapWrapper, fixed Lure Ball error in battles started in the Debug menu, improved Terrain Tag editor, fixed some Compiler errors, enabled vsync, fixed event graphics frozen to the screen when using $game_player.moveto directly. 2023-05-04 21:28:00 +01:00
Maruno17
5f20121e59 Tweaked the Level 101+ equations for some growth rates, moved code that plays the overworld dust animation 2023-04-29 18:10:26 +01:00
Maruno17
b7a40d0344 Renamed/rearranged some script files 2023-04-23 18:04:32 +01:00
Maruno17
ce549ab62a Rewrote AI item usage (inc. adding Revives), various fixes/changes to AI, removed Struggle from PBS files, some bug fixes 2023-04-23 17:52:39 +01:00
Maruno17
da182bd98a Merge branch 'dev' into ai 2023-04-20 18:05:58 +01:00
Maruno17
4bab130785 Fixed Magic Guard not being checked for damage from Shadow Sky weather/Spiky Shield/Dry Skin/Solar Power, fixed As One not having Unnerve's effect, fixed Gulp Missile paralysing the wrong Pokémon, added message for obtaining multiple machine items at once 2023-04-20 18:04:15 +01:00
Maruno17
d277658965 More AI code for deciding when to switch 2023-04-17 19:08:42 +01:00
Maruno17
b9bf3e8b83 Added message saving back to compiler, tweaked various messages, fixed typo of constant, fixed missing attr_reader 2023-04-15 20:29:47 +01:00
Maruno17
7f6f57dec3 Merge branch 'dev' into ai 2023-04-13 19:36:18 +01:00
Maruno17
af5256ae0f Updated to mkxp-z v2.4.0 2023-04-12 21:26:24 +01:00
Maruno17
a714086a39 Fixed rendering of some tiles for certain sizes of tilesets, more rubocopping 2023-04-12 21:14:26 +01:00
Maruno17
c654636bdc Merge branch 'dev' into ai 2023-04-09 22:28:34 +01:00
Maruno17
a22c5ea89c More work on the AI, refactored stat stage multipliers 2023-04-09 22:26:48 +01:00
Maruno17
956a511ec5 Fixed some errors when converting code in old trainer events to new code 2023-04-07 12:43:24 +01:00
Maruno17
5315b53eae Tidied up usage of colour tags (everything uses c3 now via def shadowc3tag), moved gender text colours to MessageConfig 2023-04-07 01:12:17 +01:00
Maruno17
6b690c4492 Fix to previous commit 2023-04-06 21:46:08 +01:00
Maruno17
1ddf89c2df Removed script/event text collation from compiler (added a debug function to do that instead), removed system cache reloader from compiler 2023-04-06 17:47:42 +01:00
Maruno17
3d9d31621b Sped up compiling PBS files by about 5x, fixed bug from earlier commit about writing PBS lines with many optional values 2023-04-05 21:41:55 +01:00
Maruno17
cb4a1fd8af def pbDrawTextPositions now uses symbols for text alignment and outline, and added a quicker way to draw text with no shadow/outline 2023-04-04 21:16:09 +01:00
Maruno17
e7847fba9a Fixed typo in a previous commit 2023-04-02 00:59:05 +01:00
Maruno17
b226e3cbb6 Fixed the value of the setting HEAL_STORED_POKEMON, restored all EOR weather messages 2023-04-02 00:57:22 +01:00
Maruno17
9d50b27aa0 Fixed some bad usage of sprintf, cleaned up some translatable messages 2023-04-02 00:52:12 +01:00
Conmh
5d9cc71a99 Made def wants_status_problem?(new_status) consider a few more abilities (#203) 2023-03-30 22:19:40 +01:00
Maruno17
ad29a79e1c Tweaked AI threshold score, added "HPAware" skill flag, changed lots of AI scores 2023-03-30 21:11:27 +01:00
Maruno17
0bb0fb4a26 Rewrote various AI switching checks 2023-03-25 23:26:34 +00:00
Maruno17
138d41f7bc Merge branch 'dev' into ai 2023-03-23 23:20:58 +00:00
Maruno17
1ead0a76f5 Fixed Beak Blast's burn affecting the wrong Pokémon 2023-03-21 18:28:06 +00:00
Maruno17
e2648032c1 More AI function codes, tweaked AI score threshold, renumbered all PBEffects constants 2023-03-19 17:22:53 +00:00
Maruno17
941e238606 Rearranged the Debug menu 2023-03-12 12:07:01 +00:00
Maruno17
7f5aea63bb shadow_pokemon.txt now supports sections for individual forms of a species 2023-03-11 23:14:19 +00:00
FL
02129f5507 Fixed party navigation on pokémon selection (#198)
* Fixed party navigation on pokémon selection

On a selections like Battle Tower's, if player places the cursor into cancel and press up, the cursor doesn't move/loop. This was fixed.

Co-authored-by: Maruno17 <serialcolour@hotmail.com>
2023-03-11 22:52:07 +00:00
Keyacom
f5bf1f6ab1 Remove powder check from Magic Powder (#202)
This is already handled by the `Powder` flag on Magic Powder, so this is unneeded.
2023-03-11 22:28:04 +00:00
Maruno17
e9a44377ce AI: Added checks for additional effect chance, Snatch/Magic Coat, more item ratings 2023-03-11 20:13:02 +00:00
Maruno17
5a18f7fd65 Fixed additional effect issues with Eerie Spell/Jaw Lock/Thousand Waves 2023-03-09 20:37:26 +00:00
Maruno17
e93c3c69ac Merge branch 'dev' into ai 2023-03-09 18:31:30 +00:00
Maruno17
9f96684048 Slightly simplified phone trainer contacts 2023-03-07 20:58:45 +00:00
Maruno17
3a9199da1b Refactored animation editor code into a single module 2023-03-06 22:25:45 +00:00
Maruno17
ee72ad371f Fixed Cotton Down also lowering the bearer's speed, tweaked rendering of buttons in the Animation Editor 2023-03-05 22:58:57 +00:00
Maruno17
e43fdeec25 Fixed back-to-back changes to backgrounds in battle animations not finishing properly before the next one starts 2023-03-05 21:57:00 +00:00
Maruno17
05f5d621b7 Fixed incorrect Hall of Fame time, fixed Shields Down showing too many messages, fixed target of Pluck still being able to consume its healing berry 2023-03-04 23:37:50 +00:00
Maruno17
6157c63fa2 Fixed movesets in PBS files, added more compatible TutorMoves 2023-03-04 18:47:32 +00:00
Maruno17
ab58da4877 Made wild Ultra Beasts smarter too 2023-02-20 23:41:25 +00:00
Maruno17
d0c99aa512 Improved AI code for weather-causing moves, added AI getting flags from trainer types, added Legendary/Mythical flags to pokemon.txt, added Setting to make wild Legendary/Mythical Pokémon smarter 2023-02-20 23:33:09 +00:00
Maruno17
0e4053f837 Tackling of various AI "TODO" comments, a little tidying 2023-02-17 21:36:08 +00:00
Maruno17
81d069eef1 Refactored AI switching code, added "UsePokemonInOrder" skill flag 2023-02-14 16:40:52 +00:00
Maruno17
a8e024eb3e Some rearranging of AI script files/methods 2023-02-10 21:47:04 +00:00
Maruno17
7e4ef4247b Merge branch 'dev' into ai 2023-02-10 21:20:28 +00:00
Maruno17
becce85550 Removed deprecated code, renamed "base_damage"/"baseDamage"/"baseDmg" to "power" 2023-02-10 21:18:10 +00:00
Maruno17
8a3353973b Fixed minor bug in sprite position editor, made trainers' Pokémon nicknames translatable, generalised code for optional PBS files 2023-02-08 23:51:42 +00:00
Maruno17
01e98c8f97 Rewrote AI for move function codes for counters, protection removal, Wonder Room, Substitute. Fixed Counters working with damage absorbed by a substitute. 2023-02-08 22:19:45 +00:00
Maruno17
3f5c1f0974 Merge branch 'dev' into ai 2023-02-05 19:10:25 +00:00
Maruno17
d0c39a3e89 Tweaks to item-consuming AI function codes 2023-02-05 19:09:01 +00:00
Maruno17
4749bd5201 Fixed Bug Bite/Pluck and Fling not enabling Belch/triggering Symbiosis when a berry is consumed 2023-02-05 18:59:30 +00:00
Keyacom
c233841bb6 Make Clangorous Soul affected by Dancer (#195)
As discovered by Awesomelink234 of Bulbapedia: https://bulbapedia.bulbagarden.net/wiki/Special:Diff/3633158
2023-02-04 21:29:54 +00:00
Conmh
5086f692df added Belch to consideration for some function codes and fixed a typo (#199)
* added Belch to consideration for some AI function codes and fixed a typo
2023-02-04 21:22:22 +00:00
Maruno17
7678a13e94 AI function code rewrites for protection moves 2023-02-04 21:14:35 +00:00
Maruno17
80bb967aad More AI function code rewrites 2023-01-31 22:12:42 +00:00
Maruno17
4da9a8c4e3 AI function code rewrites for base stat changes, electrifying moves and multi-turn moves 2023-01-29 23:24:07 +00:00
Maruno17
13aab8d911 Lots of rubocop 2023-01-28 15:21:12 +00:00
Maruno17
2d056052ce Fixed crash when trying to change a battle animation's foreground image 2023-01-26 18:37:11 +00:00
Maruno17
a0af8e6eb5 Fixed screen positioning bug when jumping over ledges near the top or left of a map 2023-01-26 18:33:21 +00:00
Maruno17
b0b6e675c3 Fixed previous commit always causing recompiling if shadow_pokemon.dat doesn't exist, also rubocopping 2023-01-23 22:27:04 +00:00
Maruno17
f6213057d8 Made a set of PBS files for Shadow Pokémon data, grouped shadow_pokemon.txt with it 2023-01-22 22:25:20 +00:00
Maruno17
d8f38947f4 AI function code rewrites, added Shadow Sky's missing effects, fixed Shadow End's recoil damage 2023-01-22 21:21:19 +00:00
Maruno17
f7578002ea Rewrites of disabling move AI function codes, fixed various AI errors 2023-01-19 22:30:55 +00:00
Maruno17
0c9df4627e Merge branch 'dev' into ai 2023-01-18 19:12:34 +00:00
Maruno17
3d9c3e2c00 Fixed incorrect variable use for Jaboca/Rowap Berries' effects 2023-01-18 19:11:14 +00:00
Maruno17
2627d68782 Added more debug logging to AI, fixed some bugs in AI 2023-01-16 19:29:28 +00:00
Maruno17
98f16c2afa More AI function code rewrites, ensured all AI procs next an appropriate value 2023-01-14 23:51:39 +00:00
Maruno17
84bdd1f60b AI function code rewrites 2023-01-12 23:08:26 +00:00
Maruno17
a22f75f500 Merge branch 'dev' into ai 2023-01-08 21:56:33 +00:00
Maruno17
632b0f8b4b Overhauled text translations 2023-01-08 17:11:38 +00:00
Maruno17
ae0d193bba More item portion name tweaks 2023-01-04 18:21:08 +00:00
Maruno17
2e8329f70b Implemented item portion names, e.g. "bag of Soft Sand" 2023-01-03 21:32:32 +00:00
Maruno17
707cd143d8 Fixed sliding on ice having the camera lag behind 2023-01-03 15:05:54 +00:00
Maruno17
9dd774a767 Fixed error when gaining happiness while at/above the happiness soft cap 2023-01-01 18:34:54 +00:00
Maruno17
522f78b75d Fixed party Pokémon icons overlapping the marking window in Pokémon storage 2022-12-31 18:03:17 +00:00
Maruno17
3fa94bacc6 Merge branch 'dev' into ai 2022-12-31 17:45:46 +00:00
Maruno17
37246d8d01 Fixed bug in previous commit 2022-12-31 17:45:26 +00:00
Maruno17
4585533a4a Fixes relating to added support of more Pokémon types 2022-12-31 17:43:42 +00:00
Maruno17
05b954e262 Merge branch 'dev' into ai 2022-12-31 17:28:56 +00:00
Maruno17
3fe324d0da Generalised a species' types to allow any number of types 2022-12-31 17:24:33 +00:00
Maruno17
dd7cd414f0 Yet more AI function code rewrites 2022-12-31 15:26:41 +00:00
Maruno17
1258e4b9c9 More AI function code rewrites themed around stat changes 2022-12-25 00:39:25 +00:00
Maruno17
f33184d413 Generalised AI code for scoring stat changes 2022-12-18 20:51:16 +00:00
Maruno17
3a4b01e2dc Fixed default terrain not being set properly sometimes 2022-12-10 22:04:24 +00:00
Maruno17
7ace4c5289 AI rewrites of ability-changing function codes, OHKO codes and binding codes 2022-12-10 21:59:44 +00:00
Maruno17
539a47671d Some more AI function code rewrites 2022-12-04 19:09:46 +00:00
Maruno17
d654f3edbf Added BP shop and prices to various items 2022-12-01 22:43:47 +00:00
Maruno17
b13c2ed2ed Fixed and generalised PBS editors 2022-11-28 20:28:48 +00:00
Maruno17
7c39a56a43 Made tutored moves learnable by Pokémon if they can get the move via level/egg move, followers won't follow the player if they already have a move route, added console warning if a different version of mkxp-z is detected 2022-11-27 22:38:47 +00:00
Maruno17
c53a52564b More AI rewrites of various function codes, made move score threshold multiplier depend on trainer skill 2022-11-26 22:31:28 +00:00
Maruno17
427cc45629 Generalised compiler and writer methods for trainers.txt 2022-11-23 22:44:15 +00:00
Maruno17
91efb4684b Fixed file-choosing methods not removing file extensions, fixed typo in Puddle envirnment registration 2022-11-23 22:43:11 +00:00
Maruno17
bd04112122 Allowed multiple PBS files per data type, removed hardcoded lists of PBS files/.dat files/GameData classes that need data loading 2022-11-22 22:24:30 +00:00
Maruno17
4d147a7bf7 Turned Town Map PBS data into a GameData class 2022-11-20 21:44:53 +00:00
Maruno17
f33eb4d896 Added PBS schema character ^ for repeated lines, more refactoring of PBS compilers/writers 2022-11-20 20:15:04 +00:00
Maruno17
1ff7307868 Rewrote and standardised several PBS writer methods 2022-11-19 23:03:42 +00:00
Maruno17
5d439de87d Added compiler schema letter "m" which makes the value a symbol, refactored many PBS file compilers, removed support for old PBS formats/properties 2022-11-16 22:03:12 +00:00
Maruno17
6f2e72eed6 Rewrites of AI for airborne-related moves 2022-11-09 22:21:32 +00:00
Maruno17
8f014135e5 More rewrites of AI for some function codes 2022-11-08 22:54:16 +00:00
Maruno17
b31400b9ae First shot at rewriting AI for moves that lower the target's stat(s) 2022-11-06 16:53:24 +00:00
Maruno17
55563463d8 Resolving clashing code from merge 2022-11-05 21:10:05 +00:00
Maruno17
3e73d1c1f9 Merge branch 'dev' into ai 2022-11-05 20:42:12 +00:00
Maruno17
bbe654028c Revised changes in previous commit about Pokémon generation for non-standard battles 2022-11-05 19:29:06 +00:00
Maruno17
5dd8c31ff0 Fixed only one IV being inherited due to a held Power item if both parents are holding different Power items 2022-11-05 18:46:22 +00:00
Maruno17
4f42eca3ee Fixed Safari/Bug Contest/roaming battles generating Pokémon while ignoring that Pokémon's defined form 2022-11-05 18:08:04 +00:00
Maruno17
096957bc20 Fixed Liquid Ooze not applying if the bearer faints, fixed two-turn moves being used in one turn charging up after failing instead of before 2022-11-05 17:48:01 +00:00
Maruno17
c984fda1cf Standardised compiling-based console messages 2022-11-05 16:18:24 +00:00
Maruno17
3408b038cf Split AI move handlers into general ones and ones per target, rearranged some main move-scoring code 2022-10-30 15:25:46 +00:00
Maruno17
6f7a714d48 Fixed error in battle fight menu when not using graphics for it 2022-10-26 18:39:32 +01:00
Maruno17
e12fd08eb1 Some more rewrites of AI move score calculations (mainly item-related moves) 2022-10-23 18:24:18 +01:00
Maruno17
5384a7afe4 Fixed a scrolled screen snapping back to centre on the player as soon as they move 2022-10-23 15:37:45 +01:00
Maruno17
2d411cf152 Fixed error in previous commit 2022-10-19 20:01:17 +01:00
Maruno17
64f975072b Fixed BGM always restarting when changing maps if the new map's BGM has a night version 2022-10-19 19:12:39 +01:00
Maruno17
5a49bbbc94 Renamed function code UseUserBaseDefenseInsteadOfUserBaseAttack, more AI function code changes 2022-10-17 21:13:31 +01:00
Maruno17
9eb90b784e addIf now need an identifier symbol, fixed clas between using Future Sight and being locked into a move, fixed IV inheritance 2022-10-16 22:53:41 +01:00
Maruno17
9ad45d6b07 Fixed bug caused by strikethrough/underline change, fixed fixed damage moves showing effectiveness messages 2022-10-16 15:52:02 +01:00
Maruno17
e8703e8b05 More error fixes in AI 2022-10-16 14:53:49 +01:00
Maruno17
43290c39a2 Errors in the AI no longer cause it to do nothing 2022-10-16 14:52:27 +01:00
Maruno17
58a624060a Rewrote AI calculations for moves that cause confusion, flinching and infatuation 2022-10-09 18:37:19 +01:00
Maruno17
b41a46aacf Fixed roaming Pokémon not remembering whether they have been caught 2022-10-08 18:06:37 +01:00
FL
de07341337 Fixed action button crashing roaming debug menu (#192) 2022-10-08 17:51:13 +01:00
Maruno17
882f4c96f8 Bug fixes: Shadow Pokémon still knowing some original moves when they shouldn't, Interpreter not resetting if saved in the middle of an event and then starting a new game, priority 1 tiles appearing below the player at larger screen sizes, ability inheritance when breeding 2022-10-08 17:46:29 +01:00
Maruno17
9695094b02 Merge branch 'dev' into ai 2022-10-04 22:56:27 +01:00
Maruno17
fa2758edaa Fixed Pastel Veil not providing poison immunity to allies, and not healing the bearer if it becomes poisoned anyway 2022-10-04 22:10:58 +01:00
Maruno17
2897476d1b Rewrote AI calculations for moves that inflict a status problem 2022-10-04 21:25:58 +01:00
Maruno17
4ddf689887 Improved Debug running options in battle, allowed Debug running from battle by choosing Call, removed support for save files in the old save location 2022-10-02 16:01:35 +01:00
Maruno17
88dc215417 Bug fixes: interacting with a follower in the same tile as the player; underline/strikethrough line mispositionings and added shadows to those lines; Toxic/Flame Orb replacing existing status problems; grown berry plants looking like sprouts; pbShowCommandsWithHelp not deactivating message windows it creates 2022-10-02 14:59:59 +01:00
Maruno17
1ccbafb499 Rewrote the random dungeon generator code 2022-10-01 18:06:15 +01:00
Maruno17
63ec037481 AI changes for some multi-hit/multi-turn moves, improved AI's damage calculation 2022-09-18 18:16:08 +01:00
Maruno17
8c31ad994d Misc tidying 2022-09-11 23:53:25 +01:00
Maruno17
2962944cab Redesigned phone.txt (old format isn't supported), added support for contact-specific phone messages, added more phone message components 2022-09-11 19:07:47 +01:00
Maruno17
4fcd19e247 Added sorting to phone, both moving individual contacts and sorting all at once 2022-09-10 23:20:40 +01:00
Maruno17
25a8d727ab Rewrote phone UI code, added "CommonEvent" trainer comment for that phone contact to run instead of default phone messages, added phone signal icon to phone 2022-09-10 19:41:12 +01:00
Maruno17
662d023ff5 Improved console messages when compiling didn't happen, egg crack graphics are now found in the same way as egg graphics 2022-09-10 19:38:21 +01:00
Maruno17
dcba641a86 Fixed typos in def pbCalcTypeModPokemon, fixed party screen navigation errors when there are no Pokémon, advanced battle Debug functions now trigger modifiers, can now always change a Pokémon's form to 0 2022-09-10 16:33:11 +01:00
Maruno17
eb8fc1d298 More AI checks and fixes 2022-09-10 15:26:38 +01:00
Maruno17
9bd31300ad AI-only battles shouldn't be usable before loading a game 2022-09-06 22:59:36 +01:00
Maruno17
eecb7a1453 Added script to make two random AI trainers battle, tweaked battle messages when logged, fixed typos 2022-09-06 22:57:51 +01:00
Maruno17
b8c61a6038 Rewrote AI calculations for move effects that raise the user's stats, fixed Sea of Fire effect lasting forever 2022-09-04 17:46:59 +01:00
Maruno17
db215a71d6 Fixed AI accuracy calculation for OHKO moves, reviewed more AI function code score modifiers 2022-09-02 21:36:31 +01:00
Maruno17
d870b027db More misc AI move effect calculation rewrites 2022-09-02 00:24:56 +01:00
Maruno17
429068f3cb More AI move effect calculation rewrites 2022-08-31 19:02:54 +01:00
Maruno17
ecb5e2ffd1 Rewrote AI calculations for remaining healing move effects 2022-08-31 16:23:24 +01:00
Maruno17
92c0fe5c90 Rewrote AI calculations for move effects relating to healing the user, added calculation to add predicted damage to a move's score 2022-08-30 22:13:23 +01:00
Maruno17
931a47c5fe Rewrote AI calculations for move effects relating to the user fainting 2022-08-30 18:33:31 +01:00
Maruno17
8275d40193 Added Mold Breaker consideration to AI, updated some AI calculations, fixed non-ignorable abilities in damage calculation actually being ignorable 2022-08-29 22:39:06 +01:00
Maruno17
22dce593e8 Split AI general move score modifiers into handlers 2022-08-29 15:59:22 +01:00
Maruno17
9ed2f3e920 Misc tidying and commenting of AI code 2022-08-28 17:59:02 +01:00
Maruno17
fbbddc8c2b Changed AI's random move choice after scores are calculated 2022-08-27 20:37:05 +01:00
Maruno17
265897f9e1 Made AI function code-specific MoveFailureCheck handlers 2022-08-27 20:11:01 +01:00
Maruno17
b6c84fa278 Added AI handlers that calculate a move's base power, moved AI damage calculation 2022-08-25 19:16:21 +01:00
Maruno17
cfb870c944 Added AI objects for trainers, battlers and the move being assessed, logging battle messages now also echoes them to the console 2022-08-22 21:37:33 +01:00
Maruno17
b094a2fd8e Reapplied various AI changes from previous atempt at improving the AI, split function code move score changes into handlers 2022-08-21 15:59:49 +01:00
Maruno17
4075204038 Merge branch 'dev' into ai 2022-08-19 21:41:09 +01:00
Maruno17
18ffd4fb94 Fix to colour-stringifying methods 2022-08-19 21:40:46 +01:00
Maruno17
963d2a515b Merge branch 'dev' into ai 2022-08-19 21:37:14 +01:00
Maruno17
ae91811537 Created folder Graphics/UI, renamed some UI graphics 2022-08-14 01:07:06 +01:00
Maruno17
b1cde2db42 Refactored some code relating to colours, trivially simplified some calculations 2022-08-13 23:59:24 +01:00
Maruno17
aa9b1a9e23 Refactored phone/rematches code, added Phone.rematches_enabled/Phone.rematch_variant/map metadata flag "NoPhoneSignal", changed event layout, trainer contacts can use Common Events for their calls, etc. 2022-08-13 16:52:42 +01:00
Maruno17
c79b970d6b Fixed error when shifting Pokémon at the end of a battle round, fixed Giratina's form code crashing if the current map doesn't have metadata, fixed item sell prices being halved twice 2022-08-13 16:29:48 +01:00
Maruno17
1d2f13f9c5 Entered dev version for v21 2022-08-10 21:16:17 +01:00
Keyacom
d890cd8a24 Edited string methods (#186)
* Changed numeric check regex
- `^` may match the position right after a newline character, and `$` may match right before the newline character (due to the multiline flag being enabled by default), so substing these with `\A` and `\Z`.
- Changed both instances of `[0-9]` with the shorthand `\d`.
- Removed capturing group around the first `\d+`.
* Changed get character at index
`anyString[0, 1]` is functionally the same as `anyString[0]` because the range `[a, b]` returns all characters from index `a` up to but not including the char at index `b`.
2022-08-10 21:11:19 +01:00
Maruno17
5ff0c04eed Added Marin's AI as a reference 2022-08-10 18:38:20 +01:00
Maruno17
eeeb9b3494 Added old AI references/rewrite attempt as references 2022-08-09 19:35:45 +01:00
Maruno17
a20f378b33 Minor tweaks 2022-08-09 19:30:27 +01:00
Golisopod-User
bff0cc6974 Fixed set movement type (#185) 2022-08-08 17:40:12 +01:00
Maruno17
b2ddedbce1 Reduced the work done when updating the sprites of events without a graphic 2022-08-07 16:29:29 +01:00
Maruno17
6542d505a6 The player now dismounts the Bicycle after various map transfers 2022-08-07 16:07:37 +01:00
Maruno17
cca414a826 Bug Contest maps can now be defined using a map metadata flag 2022-08-07 15:00:40 +01:00
Maruno17
f083cddca5 def pbPhoneRegisterBattle now returns a boolean, added vowel checks to some messages in berry planting 2022-08-07 14:43:40 +01:00
Maruno17
b423f34da6 Returning to the title screen now fades out the overworld first 2022-08-07 14:39:10 +01:00
Maruno17
f22707f2f1 Fixed being unable to set the player's movement speed during a move route 2022-08-07 14:27:39 +01:00
Maruno17
3c748c9d68 Fixed play time carrying over to new games, fixed Eerie Spell thinking it's a status move, fixed text positionings in Pokédex and naming screen, fixed Channeler typo 2022-08-07 14:21:30 +01:00
Maruno17
757798c8a8 Summary screen now uses only the first Regional Dex to determine Dex No. if the National Dex is locked 2022-07-10 19:16:11 +01:00
Maruno17
72e45ab137 Fixed error when getting terrain tag when the player moves between connected maps 2022-07-10 19:05:54 +01:00
Maruno17
473d4cbb67 Fixed SystemStackError when two events on connected maps have their backs to the other map 2022-07-10 19:05:42 +01:00
Maruno17
8ae07a6d15 Fixed some Vs animations not playing for double battles against 1 trainer, fixed typo in Grassy Glide's effect 2022-07-08 13:44:04 +01:00
Maruno17
3314477f08 Made the example wild encounter modifier that scales Pokémon levels to match the player's party depend on a map metadata flag instead of a map number 2022-07-07 20:49:49 +01:00
Maruno17
b50ce948f6 Fixed battle rule "forceCatchIntoParty" being circumventable, fixed wrong message when sending a party Pokémon to storage and replacing it with a caught Pokémon 2022-07-07 20:42:23 +01:00
Maruno17
e1dc7c91bc Fixed broken Bill's PC menu options 2022-07-06 20:03:46 +01:00
Maruno17
0b65865962 Fixed error message spam in the console when editing Game Swtches via the Debug mode feature 2022-07-06 20:03:10 +01:00
Maruno17
8f73319605 Fixed evolution reviving fainted Pokémon, fixed Rare Candy not reviving a fainted Shedinja 2022-07-06 20:02:30 +01:00
Maruno17
a85f672c73 Fixed crash when the Compiler rewrites a door event 2022-07-06 18:43:52 +01:00
Maruno17
2976ee93ce Added Setting that makes trainer-owned Pokémon give more Exp, other tweaks 2022-07-03 17:39:44 +01:00
Maruno17
a2327c6280 Fixed playing the credits/changing $scene leaving a ghost image of the old map behind, fixed Pokémon icons sometimes disappearing in storage screen 2022-07-03 16:40:25 +01:00
Maruno17
6dd88e49e1 Fixed the "See ya!" option in the PC menu not working properly, fixed mispositioning of text in Debug features that edit Game
Switches/Variables
2022-07-03 15:58:37 +01:00
Maruno17
94c6b6db0c Fixed Heavy Ball's catch rate calculation being inaccurate, added Obstruct to some moves' blacklists 2022-07-03 15:22:34 +01:00
Maruno17
7e183a92aa Tidying up for v20.1 release 2022-06-20 19:17:38 +01:00
Maruno17
76639c4ba5 Fixed duplicate files listed in BGM/ME pickers in PBS file editors 2022-06-20 08:44:18 +01:00
Maruno17
03ffae6eed Fixed empty wild encounter type tables not being editable in "Edit Wild Encounters" debug function 2022-06-19 23:07:21 +01:00
Maruno17
2486fa9fe2 Fixed player being able to be stuck in running position when opening the pause/ready menu 2022-06-19 22:44:29 +01:00
Maruno17
040247536f Some improvements to map/event rendering when changing display tile width/height in map renderer 2022-06-19 22:34:22 +01:00
Golisopod-User
ad93eb7774 Removed all uses of SpriteWrapper (#184) 2022-06-19 17:47:23 +01:00
Maruno17
8b3d95943a Essentials no longer requires the "Plugins" folder to exist 2022-06-19 17:40:34 +01:00
Maruno17
9261851701 Fixed party order not being altered at the start of battle if there are unable Pokémon before the one(s) sent in initially 2022-06-19 17:36:35 +01:00
Maruno17
9a53ee8a0e Fixed text positioning in "Roaming Pokémon" Debug function 2022-06-19 17:35:08 +01:00
Maruno17
2e3ba2e09e Fixed Grudge and Lunar Dance not properly changing PP amounts 2022-06-19 17:04:58 +01:00
Maruno17
b367b2442b Fixed error when using Future Sight 2022-06-18 21:18:34 +01:00
Maruno17
9a03b25755 Fixed using Pursuit against a switcher preventing using the next move 2022-06-18 21:13:30 +01:00
Maruno17
79f5e5d4aa Fixed mistake in individual trainer editor 2022-06-18 20:48:27 +01:00
Maruno17
4b895e44ef Fixed the passability of the blank tile not being ignored if its priority is 0 2022-06-17 21:41:35 +01:00
Maruno17
d42e35f8c4 Fixed being unable to walk across a map connection into an event even while holding Ctrl in Debug mode 2022-06-17 21:41:22 +01:00
Maruno17
cd47b9899c Fixed some maps being loaded when they shouldn't 2022-06-16 19:50:36 +01:00
Maruno17
3d8e22690d Stopped various filenames being translatable when they don't need to be 2022-06-16 13:29:48 +01:00
Maruno17
3238ff817c Fixed the Bag lists having incorrect starting positions when choosing an item from a subset of your Bag 2022-06-15 20:03:43 +01:00
Maruno17
b8e9251fb4 More splitting of AI_Move_EffectScores 2022-06-15 19:23:52 +01:00
Maruno17
62b964adfa Split script section AI_Move_EffectScores in two and rearranged it 2022-06-15 19:15:03 +01:00
Maruno17
c0c672806d Refactored code that draws the Pokémon info box in battle 2022-06-13 23:17:09 +01:00
Maruno17
cc540b0132 Refactored code that draws party screen panels 2022-06-13 22:54:44 +01:00
Maruno17
8d4015c903 Improvement to Scent items' in-battle effects 2022-06-13 21:23:51 +01:00
Maruno17
301ecb0ac7 Scents now end Hyper Mode, Time Flute now purifies a Shadow Pokémon, Shadow Pokémon now regain 80% of the Exp they missed out on, non-Scent items can't be used on Shadow Pokémon in Hyper Mode 2022-06-13 21:09:48 +01:00
Maruno17
732c167ad5 Writing moves.txt now calls power "Power" instead of "BaseDamage", other minor tidying 2022-06-13 20:17:16 +01:00
Maruno17
aec0215442 Fixed PBS files not being recovered properly if playing in Debug mode without a PBS folder 2022-06-13 20:15:47 +01:00
Maruno17
cdc82b6e65 Fix to previous commit about trainer intro BGM persisting after skipped battles 2022-06-10 17:48:18 +01:00
Maruno17
248b0dd882 Added def GameData::Species.base_stat_total, other minor code tweaks 2022-06-09 21:06:35 +01:00
Maruno17
cb2f33d8f6 Fixed GameData::Species.icon_bitmap_from_pokemon causing a crash, fixed trainer intro music not stopping after skipping their battle 2022-06-09 21:05:22 +01:00
Maruno17
49c916e1bb Added support for the Bug Catching Contest taking place over multiple maps 2022-06-08 23:13:01 +01:00
Maruno17
101dca7a31 Fixed the Back input not being able to close a displayed message in the Poké Mart 2022-06-08 22:46:30 +01:00
Maruno17
038be447fb Fixed crash when using a Shadow Pokémon Scent or Time Flute item 2022-06-05 14:48:54 +01:00
Maruno17
d383d69cf4 Fix for tiles being invisible after a map transfer if they were on screen before it 2022-06-04 16:06:00 +01:00
Maruno17
2c65a9de7d Fix for tiles being invisible after battle until they leave the edge of the screen 2022-06-03 20:13:19 +01:00
Maruno17
ff1f9633f3 Fixed SystemStackError when loading a connected map with an event at its edge facing away from that edge 2022-06-03 14:42:04 +01:00
Maruno17
b441411b2e Fixed trainer intro BGM persisting after battles against multiple trainers 2022-06-02 18:47:24 +01:00
Maruno17
e3dbc5e690 Updated moves.txt to fix a few mistakes 2022-06-02 17:27:14 +01:00
Maruno17
17e386d33e Fixed def pbChooseItemFromList not storing the correct result in a Game Variable 2022-06-02 16:46:27 +01:00
Maruno17
0da046d2c3 Fixed memory leak caused by lots of map transfers 2022-06-02 16:43:58 +01:00
Maruno17
1532be2410 Fixed incorrect capitalisation in message when Aurora Veil wears off 2022-06-02 16:40:37 +01:00
Maruno17
a63d031da2 Fixed Gorilla Tactics also boosting Special Attack 2022-06-01 22:48:24 +01:00
Maruno17
3338875dd8 Fixed error when applying Sea of Fire's damage 2022-06-01 22:47:32 +01:00
Keyacom
2ba2893869 Fixed Grav Apple having the incorrect damage boost (#179)
* Fixed Grav Apple having the incorrect damage boost

In the official games, Grav Apple has a damage boost of 1.5x under Gravity, not 2x. This has been fixed.

* Updated move effect class name
* Apply updated function code
* Update moves.txt
2022-05-29 23:01:34 +01:00
FL
22018012c7 Fix challenge min length (#180)
* Fix challenge min length

Challenge min length wasn't working when combined with PokemonRule/TeamRule.
2022-05-29 19:18:38 +01:00
Maruno17
9f09851db9 Renamed moves.txt property BaseDamage to Power 2022-05-29 19:01:49 +01:00
Maruno17
aa2269c858 Added Compiler code to make simple text replacements in PBS files before they are compiled 2022-05-29 19:00:59 +01:00
Maruno17
8310114252 Fixed some warning messages relating to compiling moves.txt 2022-05-29 18:53:24 +01:00
Maruno17
6a046ff755 Fixed Xerneas/Zacian/Zamazenta not being their alternate form throughout battle 2022-05-29 18:13:08 +01:00
Maruno17
9ecbee910d Fixed error when calculating type effectiveness against a Pokémon with no types 2022-05-28 23:07:55 +01:00
Maruno17
325a1b2998 Fixed Dauntless Shield raising the wrong stat 2022-05-26 18:57:21 +01:00
Maruno17
eebe93678a Fixed Pickup's out-of-battle effect causing an error 2022-05-26 18:50:48 +01:00
Maruno17
ff3c3acfa6 Merge branch 'dev' 2022-05-24 19:10:34 +01:00
Maruno17
0d567db2cd Updated README.md 2022-05-24 19:07:22 +01:00
Maruno17
ebf83649ec Fixed error when the Compiler tries to convert some pbTrainerBattle code to TrainerBattle.start 2022-05-24 18:56:33 +01:00
Maruno17
eab69f213a Fixed error when using Rotom Catalog 2022-05-24 13:44:12 +01:00
Maruno17
d94d1836c6 Fixed Howl always failing 2022-05-24 13:43:01 +01:00
Maruno17
41479fac04 Fixed the player animating super-fast for a while after surfing 2022-05-23 23:29:23 +01:00
Maruno17
a280fea093 Fixed console message colouring breaking when loading a plugin whose name contains an apostrophe 2022-05-21 19:20:35 +01:00
Maruno17
98aeff01c5 Pokédex entry won't appear for gifted Pokémon if you don't want to record its form in the Pokédex 2022-05-20 20:32:16 +01:00
Maruno17
38d551f72b Fixed not registering a gifted Pokémon as seen/owned before looking at its Pokédex entry 2022-05-20 20:22:47 +01:00
Maruno17
ce5d68a904 Fixed incorrect Pokémon icons shown in Ready Menu if there are eggs in the party 2022-05-20 19:11:12 +01:00
Maruno17
d5e26d13f7 Renamed VictoryME to VictoryBGM in trainer_types.txt 2022-05-20 17:45:03 +01:00
Maruno17
0ab1e6242d Added some more outdated script replacements to the Compiler 2022-05-20 17:44:35 +01:00
Maruno17
d28ccd13e1 Fixed incorrect status condition icon used for fainted Pokémon and Pokémon with Pokérus 2022-05-20 17:39:17 +01:00
Maruno17
b344cb7fcc Fixed event evolutions not working 2022-05-20 17:36:39 +01:00
Maruno17
cc7ecf0326 Fixed incorrect message when choosing a Pokémon to withdraw from Day Care 2022-05-20 17:35:02 +01:00
Maruno17
622aa708b0 Merge branch 'dev' 2022-05-19 23:23:50 +01:00
Maruno17
f5ee3b9c9f Fixed F9 skipping a turn in Safari battles, fixed Gym Leader attempts stat not working 2022-05-19 22:24:40 +01:00
Maruno17
47ad801645 Tidying up for v20 release 2022-05-19 19:42:16 +01:00
Maruno17
5fb8218098 Fixed type-boosting Gems being consumed when they shouldn't, fixed compiler error relating to replacing battle-starting methods 2022-05-19 13:42:21 +01:00
Maruno17
e9cd62b8b6 Added Game_Character#x_offset and y_offset, to adjust event display positions by pixels 2022-05-18 21:49:27 +01:00
Maruno17
d7f4434000 Added "Sight(x)" event name snippet 2022-05-18 19:32:28 +01:00
Maruno17
04f3b29fe3 Tweaks to comments, Destiny Knot no longer affects breeding in Gen 5 mechanics 2022-05-17 23:52:53 +01:00
Maruno17
e12b6fde1d NPC trainers' end of battle text is now read directly from the trainer object rather than extracted to an array 2022-05-17 22:28:29 +01:00
Maruno17
cf338b3a6a Fixed bug in previous commit 2022-05-15 20:35:26 +01:00
Maruno17
22f0c8627a Refactored battle-starting methods into WildBattle.start and TrainerBattle.start, etc. 2022-05-15 20:28:06 +01:00
Keyacom
07d7403fbf Minor regex fix (#178)
/[Cc]/ takes up 2 more chars than /c/i.
2022-05-13 21:39:12 +01:00
Maruno17
823ce771a8 Fixed cursor SE sometimes playing when choosing a quantity in the Bag/Poké Mart but the quantity doesn't change 2022-05-13 21:35:03 +01:00
Maruno17
1caedc0ed2 Added a Setting to toggle whether the Move Reminder can teach previously known egg moves/TRs 2022-05-13 21:12:47 +01:00
Maruno17
7a88d47b4b Minor fixes to Pokérus code, fixed some incorrect apostrophes in PBS files 2022-05-06 19:04:13 +01:00
Maruno17
5a1ef50e57 Fixed location signpost display incorrectly caring about a map's RMXP name when it shouldn't 2022-05-06 18:59:08 +01:00
Maruno17
1b551b30f4 Fixed fishing/Rock Smash/Headbutt being able to cause double wild battles 2022-05-01 01:08:33 +01:00
Maruno17
140ba4c622 Player's charset now changes accordingly when moved fast/slow by a move route 2022-05-01 01:06:38 +01:00
Maruno17
c3af3c6f58 Rewrote/generalised pbPickup and moved its item arrays and chances into separate constants 2022-04-28 21:29:06 +01:00
Maruno17
b7f230fcfc Shortened and updated documentation of PluginManager 2022-04-25 23:27:11 +01:00
Maruno17
5714a2d6d3 Split up def pbEndOfRoundPhase 2022-04-25 23:24:55 +01:00
Maruno17
fd5d3e332d Fixed moves taught via battle Debug menu not working, fixed Max Mushrooms showing the stat up animation for every stat 2022-04-24 21:06:40 +01:00
Maruno17
ad29dc6dc4 Fixed X items not working, fixed Knock Off being able to remove usable Mega Stones 2022-04-24 21:04:22 +01:00
Maruno17
8062b8ed6c Made "DefaultForm_X" flag apply when changing a Pokémon's species 2022-04-24 12:04:34 +01:00
Maruno17
a85ec1e51a Changed battle victory MEs to BGMs 2022-04-21 21:43:11 +01:00
Maruno17
77f9547687 Misc tidying 2022-04-20 22:29:13 +01:00
Maruno17
d3b61a64ef Turned trainer intro MEs into BGMs 2022-04-20 22:11:04 +01:00
Maruno17
144ad91bc1 Fixed icons in messages being misaligned 2022-04-18 21:16:46 +01:00
Maruno17
408e63b5d7 Updated to mkxp-z v2.3.1 2022-04-18 21:16:24 +01:00
Maruno17
d6c6b79705 Tidying up tones 2022-04-18 20:47:19 +01:00
Maruno17
20d6d4ff0a Implemented differences in Ball Burst animations for attempting to capture a Pokémon 2022-04-18 20:45:10 +01:00
Maruno17
f66e8db906 Minor script rearranging 2022-04-12 21:21:58 +01:00
Maruno17
90328df274 Updated moves that can't be called by Metronome 2022-04-10 22:33:56 +01:00
Maruno17
0d4c27d413 Implemented differences in Ball Burst animations for sending out a Pokémon 2022-04-10 21:38:06 +01:00
Maruno17
4a478ab6d0 Fixed recalculating turn order after Mega Evolution not taking into account changed abilities 2022-04-08 18:43:49 +01:00
Maruno17
0680f8665d Added prompt for what to do with a caught Pokémon if the party is full, and a battle rule that forces a capture into the party 2022-04-05 23:01:07 +01:00
Maruno17
22e0d1dfc5 def register_last_seen and related methods can now work with shininess 2022-04-03 20:42:22 +01:00
Maruno17
026e6f5c0a Added a Setting that makes a new Pokémon's Pokédex entry show after evolution, trading, hatching, gaining from Mystery Gift and general adding 2022-04-03 19:06:56 +01:00
Maruno17
e87d55f56f Added underscore in DefaultForm species flag for consistency 2022-03-31 20:20:29 +01:00
Maruno17
c012a7323a Natural Gift's power/type moved into an item flag 2022-03-31 20:19:58 +01:00
Maruno17
38edb15f0c Move item powers for Fling into an item flag 2022-03-31 19:53:21 +01:00
Maruno17
3a4339c658 AI now checks for immunities to status moves 2022-03-28 18:04:45 +01:00
Maruno17
3c6799091e Added battle transition animation for Elite Four/Champion 2022-03-27 22:42:43 +01:00
Maruno17
81578ac42c AI now doesn't use Full Restores just to heal small amounts of HP 2022-03-26 15:12:32 +00:00
Maruno17
7d583aaaf7 Added animation when capture is successful 2022-03-21 23:14:00 +00:00
Maruno17
2817c1998a Added hardcoded battle animations for capture ball burst and send out ball burst, removed support for common animation ball bursts. 2022-03-21 17:37:18 +00:00
Maruno17
21f7c57905 Added Poké Ball burst animation for recalling in battle, some bug fixes 2022-03-16 22:44:39 +00:00
Maruno17
93a0173fe8 Player no longer makes a bump sound when walking into something because of a move route, changed when title screen BGM starts playing, tweaked evolution messages and audio 2022-03-10 22:49:01 +00:00
Maruno17
a0612b907f Changed format of "EVs" property in pokemon.txt/pokemon_forms.txt 2022-03-09 23:03:02 +00:00
Maruno17
91f8d06797 Added battle debug option to view/edit all Pokémon, fixed some battle debug-related bugs 2022-03-07 23:19:30 +00:00
Maruno17
7f8fa16a52 Made Zacian/Zamazenta's rusted items unlosable, made bad poison icon be shown again in battle 2022-03-07 23:16:13 +00:00
Maruno17
5d8dad58c4 Added more debug menu functionality (toggle Hyper Mode, set stats, set species, reset moves of non-battling Pokémon/let them be made fainted) 2022-03-07 19:25:33 +00:00
Maruno17
a13771e8bc Fixed Shadow Pokémon saying how much Exp they gain from battle, fixed mispositioned text in some battle debug menus 2022-03-07 19:22:21 +00:00
Maruno17
bcb31c7a56 Made checks of map connections go through a single method, which can be used to modify connections on the fly 2022-03-05 23:49:53 +00:00
Maruno17
a20a1775c0 Added "VS Rocket Admin" battle transition animation 2022-03-05 20:16:11 +00:00
Luka S.J
fe159419f7 [feature] Expand built-in utilities for Essentials (#166)
* Refactor hash and add small string utility
* Add utilities for `Color` object
* Add `.findDirectory` to `PluginManager`
* Add extra file and directory utilities
* Add `CallbackWrapper` utility
* Fix hash `deep_merge`
* Refactor hash `deep_merge` to simplify
* Fix hash merging
* Fix ruby utilities
2022-02-28 23:32:25 +00:00
Maruno17
fe6b87c075 Teak to animation in previous commit 2022-02-24 20:56:09 +00:00
Maruno17
4fc6e7f289 Added HGSS "Vs Trainer" battle transition 2022-02-24 20:41:18 +00:00
Maruno17
15e70fa67e Added a battle transition for Team Rocket Grunts 2022-02-22 22:34:28 +00:00
Maruno17
e5be233224 Enabled BallBurst common animations to be played during other battle animations 2022-02-22 19:26:50 +00:00
Maruno17
2057084f69 Light effects now centre themselves vertically on their events as intended 2022-02-21 23:36:43 +00:00
Maruno17
33cde3bf20 Changed the available special characters available in the naming screen 2022-02-21 18:51:17 +00:00
Maruno17
9b3e62b1f3 Fixed bug relating to night BGMs from previous commit, improved Purify Chamber a bit 2022-02-20 23:31:09 +00:00
Maruno17
19e50bd507 Updated Power Green fonts, repositioned all text accordingly 2022-02-20 23:29:09 +00:00
Maruno17
dd78075657 Minor tweaks to compiling item data 2022-02-15 19:16:55 +00:00
Maruno17
f322b234c6 Added battle rule "disablePokeBalls" 2022-02-15 18:39:41 +00:00
Maruno17
5f39363e69 Added map metadata flag "HideEncountersInPokedex", fixed Pokédex not showing the areas of some maps that span multiple Town Map points 2022-02-14 23:25:10 +00:00
Maruno17
789185eefb Added Gen 6 PBS files 2022-02-14 19:06:32 +00:00
Maruno17
f68e699cc9 More Rubocop changes 2022-02-13 00:57:54 +00:00
Maruno17
cca5989746 Fixes to previous commit 2022-02-10 08:33:45 +00:00
Keyacom
30c3f472ef Implement TramplesMinimize as a move flag (#175)
* Implement "TramplesMinimize" as a move flag

Implemented `TramplesMinimize` as a move flag. Due to it, removed effect codes `FlinchTargetTrampleMinimize` and `ParalyzeTargetTrampleMinimize` (the second one was exclusive to Body Slam). Moves that had them now have the effect codes `FlinchTarget` and `ParalyzeTarget`, respectively.

The code now does not check if the parameter of `tramplesMinimize?` is 1 or 2, it now checks if it is `true` or `false`. For the effect that causes the moves with this flag to skip accuracy checks, it also checks if `Settings::MECHANICS_GENERATION` is equal to or greater than 6.

Data on which moves are to be treated as able to double damage if Minimized and skip accuracy checks are [here](https://bulbapedia.bulbagarden.net/wiki/Minimize_(move)#Vulnerability_to_moves).

**Remarks:**
- Dragon Rush, Heat Crash and Heavy Slam could not trample Minimize in Gen 5. Dragon Rush and Heat Crash could do so in Gen 6+, but Heavy Slam could not until Gen 7.
- Double Iron Bash could only trample Minimize in Gen 7.

**TL;DR:**
- Implemented `TramplesMinimize` as a move flag.
- Modified the check for this move flag.
- Removed effect codes `FlinchTargetTrampleMinimize` and `ParalyzeTargetTrampleMinimize`. Moves that had them now have the effect codes `FlinchTarget` and `ParalyzeTarget`, respectively.
2022-02-06 19:36:51 +00:00
Golisopod-User
e6c877fe06 Display version number of plugins in the console (#173)
* Display version number with plugins
2022-02-06 19:01:42 +00:00
Maruno17
4561cc66bf Moved calls to PBS file-compiling methods into their own method, removed music file extensions from metadata.txt 2022-02-06 18:40:43 +00:00
Maruno17
6c5c54a7d3 Tweaked Jukebox functionality, simplified code relating to night versions of BGMs 2022-01-30 20:40:07 +00:00
Maruno17
0465572ca0 Added method for buying a Game Corner prize item, made def _I able to replace text like _INTL 2022-01-30 20:03:52 +00:00
Maruno17
6698149083 Restored evolutions depending on Moss Rock/Icy Rock/magnetic field in Gen 8 PBS files 2022-01-30 18:16:19 +00:00
Keyacom
91c6f1f45b Fixed typo in "CannotMetronome" (#171)
* Fixed typo in "CannotMetronome"

This flag had 3 N's in its name where it was supposed to be two. In the move PBS files, the flag is spelled correctly.
2022-01-29 19:07:01 +00:00
Golisopod-User
617f685694 Bugfixes (#170)
* Fixed crash when setting Fogs or Panoramas to none
* Generalised the initial setting of stat stages for battlers
* Fixed fishing animation looking weird (especially when surfing)
2022-01-29 19:06:21 +00:00
Keyacom
af60c4df66 Use the .max function in Substitute's code (#167) 2022-01-29 18:24:42 +00:00
Golisopod-User
f9e34b8157 Switch to lowercase letters after selecting first uppercase letter (#169) 2022-01-29 18:21:38 +00:00
Maruno17
7c7a6d91bd Fixed field animations relating to events and followers not showing if they're on a connected map, fixed grass not rustling if the player walks onto it directly from a connected map, fixed grass rustling appearing beneath the player 2022-01-29 18:05:11 +00:00
Maruno17
2372322c24 Added holding Shift while starting the game to compile just the plugins 2022-01-25 23:38:11 +00:00
Maruno17
8f69c555da Fixed duplicated and missorted track listings in Jukebox 2022-01-24 23:32:05 +00:00
Maruno17
98f311c394 Updated to mkxp-z v2.3.0 2022-01-24 23:12:06 +00:00
Maruno17
c1de9a04d4 Made party screen use MenuHandlers 2022-01-23 22:54:58 +00:00
Maruno17
d0da7b0c1b Fixed "Set Event Location" (moveto) sending an event off-screen not making it disappear 2022-01-23 21:16:08 +00:00
Maruno17
8a3e5b01e0 Fixed inconsistencies in error messages produced when loading a plugin 2022-01-22 22:03:31 +00:00
Maruno17
7f0526114f Errors raised by PluginManager are no longer capitalised 2022-01-21 12:59:23 +00:00
Golisopod-User
b980d8505c Tiny Bugfixes (#161)
* Removed extra confirm sfx in mart screen
* Fixed Calyrex keeping its form exclusive moves
* Fixed some methods being aliased multiple times upon soft resetting
2022-01-20 22:02:22 +00:00
Maruno17
a0d951edf7 Added Debug menu functions to toggle Exp. All's effect and Box Link's effect 2022-01-19 20:39:55 +00:00
Nathan-MV
e9afefb210 Updated HM/expAll/Switch Messages. (#156) 2022-01-19 20:10:56 +00:00
Golisopod-User
1279f8137e Add .mkproj files to .gitignore (#159) 2022-01-19 19:15:53 +00:00
Maruno17
f872db2618 Added battle debug functions for setting position effects and NPC trainer items 2022-01-17 22:55:01 +00:00
Maruno17
b550ad9c07 Fixed AI scores calculated for some moves not being added up properly 2022-01-13 23:28:00 +00:00
Maruno17
8a89b7fbf4 Added move flag "CannnotMetronome" 2022-01-12 23:15:46 +00:00
Maruno17
25b009b344 Added hide_choice and rename_choice which alter the next set of choices shown by an event 2022-01-12 22:46:14 +00:00
Maruno17
24efbccb67 Rearranged Settings 2022-01-11 22:07:37 +00:00
Golisopod-User
ff465a78a6 More bugfixes (and one Gen 8 addition) (#155)
* Added comment indicating unused method pbRefreshSceneMap
* Removed superfluous ending of Beak Blast's effect
* Fixed crash when playing an animation in the animation player
* Added SwSh Terrain effects
2022-01-11 21:36:01 +00:00
Joni Savolainen
767a47d90f Fix Bitmap#draw_text override not accepting Rect argument (#150) 2022-01-09 21:22:40 +00:00
Maruno17
507014e655 Fix to previous commit 2022-01-08 23:18:06 +00:00
Maruno17
5fba3462c6 Event choices shown without an accompanying message are now translated 2022-01-08 21:08:58 +00:00
Maruno17
91e3d8190e Multiple "Show Choices" event commands next to each other will now behave as one with all their choices 2022-01-08 21:04:08 +00:00
Maruno17
f853816d63 Fixed visual glitch where a Pokémon's HP would raise then lower if its substitute took damage 2022-01-08 18:11:02 +00:00
Maruno17
f623fad42e Workaround for broken screenshot-taking code due to latest mkxp-z 2022-01-03 19:55:54 +00:00
Maruno17
6d23d0101a Added descriptions for Options screen options, tweaked appearance of Options screen, fixed SpriteWindow#back_opacity not applying to all parts of the window graphic 2022-01-03 19:54:31 +00:00
Maruno17
3f51ab44e4 Moved Options screen options into MenuHandlers 2022-01-03 17:53:45 +00:00
Maruno17
a939406e60 Improved Pokégear screen to work for all screen sizes 2022-01-03 13:48:44 +00:00
Nathan-MV
aa18a59413 Update PokeMart Messages (#149)
* Updated PokeMart Messages
2022-01-03 13:17:14 +00:00
Maruno17
a6115faad6 Fixed Incinerate's message not knowing the name of the item it destroyed 2022-01-02 21:22:09 +00:00
Maruno17
2a34904baa Fixed form changing being able to leave a Pokémon knowing the same move twice 2022-01-02 21:19:13 +00:00
Maruno17
4cf13f2942 Created module MenuHandlers for the contents of various menus 2021-12-31 17:45:07 +00:00
Maruno17
7da449aec3 Merged Events and EncounterModifier into module EventHandlers 2021-12-30 18:59:35 +00:00
Maruno17
a6c092a574 Abilities that hasten egg hatching now have a flag in abilities.txt for this effect 2021-12-29 22:13:18 +00:00
Maruno17
f3c4893dbb Repels now have a flag in items.txt which determines if they can be reused when one runs out 2021-12-29 22:07:10 +00:00
Maruno17
ca9df4177a Tidying up from previous commit 2021-12-29 18:53:39 +00:00
Golisopod-User
13cc9790ce Minor new additions for v20 (#147)
* Moved TMs/ TRs and HMs into their own handlers
* Improved Plugin Error Message
* Added sound effect when picking berries
* Allow player to always see quantity when buying items
* Trainers now require a Mega Ring item in their items (in trainers.txt) to Mega Evolve
2021-12-29 18:36:26 +00:00
Maruno17
fb29f19a28 Fixed various bugs found when generating Battle Frontier challenge lists 2021-12-29 14:37:41 +00:00
Maruno17
a7e8005f53 Fixed another typo 2021-12-29 13:17:27 +00:00
Maruno17
3d37733d1a Fixed bad uses of .sample 2021-12-29 12:21:19 +00:00
Maruno17
6339f5a577 Fixed typo in random dungeon code 2021-12-29 11:46:57 +00:00
Maruno17
63b157d7ec Fixed some bad uses of @battle 2021-12-29 11:43:24 +00:00
Maruno17
01ce5d347c Fixed rare bug in disobedience check 2021-12-28 22:14:56 +00:00
Maruno17
3e9d3b0a68 Added checks that prevent aliasing a method if the alias method already exists, fixed Symbiosis not working properly for opponents 2021-12-28 19:22:31 +00:00
Maruno17
139f2a74f2 Minor fixes to Voltorb Flip 2021-12-28 16:50:53 +00:00
Maruno17
aa643a6049 More Rubocop 2021-12-27 00:26:45 +00:00
Maruno17
4a6324389b Added a method that checks whether the player has enough Triple Triad cards to play 2021-12-23 18:49:11 +00:00
Maruno17
35126dfb6b Fixed regional form-exclusive species producing eggs of the wrong species, fixed eggs not being the appropriate form based on the region they were made in 2021-12-23 18:20:43 +00:00
Maruno17
b988638746 Fixed the default field weather being 0 instead of :None, rearranged save conversions 2021-12-23 16:35:19 +00:00
Maruno17
132a16950d Yet more Rubocopping 2021-12-23 00:27:17 +00:00
Maruno17
514fe13ca2 More additions to .rubocop.yml, minor code tweaks 2021-12-22 21:01:37 +00:00
Maruno17
022dc7aadb More additions to the battle debug menu 2021-12-21 20:50:08 +00:00
Maruno17
6b3fa5e1bf Made Giratina's form use a map_metadata flag "DistortionWorld" instead of a hardcoded array of map numbers 2021-12-21 19:12:19 +00:00
Maruno17
dfb3a51815 Forced encoding of text to UTF-8 2021-12-20 21:46:29 +00:00
Maruno17
3c85c3fe55 More Rubocop modifiers 2021-12-20 21:18:05 +00:00
Maruno17
5cdabe5b58 Update to /rubocop.yml 2021-12-20 20:42:36 +00:00
Maruno17
2e63556722 Added .rubocop.yml 2021-12-20 19:35:02 +00:00
Maruno17
33fcbf623b More Rubocopping 2021-12-20 17:18:21 +00:00
Nathan-MV
db4acd3369 Give Nicknames (#142)
* Option: Give Nicknames
2021-12-19 21:08:31 +00:00
Golisopod-User
7f86db6da9 Minor bugfiixes (#140)
* Fixed moves bounced back by Magic Coat/Bounced moves affecting Pokemon they normally shouldn't
* Fixed townmap compiler not saving any map names (doesn't make a difference; added for completionism)
* Update a few ruby utilities
* Fixed small typo bug in map renderer disposal
2021-12-19 20:58:28 +00:00
Maruno17
65b1a8d6c3 More tweaks from the Rubocop overlord 2021-12-19 17:28:59 +00:00
Maruno17
33781493f4 More aligning of code 2021-12-19 12:19:08 +00:00
Maruno17
13a238cc6a Many more Rubocop-inspired code improvements 2021-12-18 19:06:22 +00:00
Maruno17
d17fc40a47 Added decent spacing to all scripts thanks to Rubocop 2021-12-18 15:25:40 +00:00
Maruno17
f7b76ae62e The Great Enspacening begins! 2021-12-18 12:20:31 +00:00
Maruno17
2480ab0f9e Applied most Rubocop-suggested layout fixes 2021-12-18 01:56:10 +00:00
Maruno17
5dc64f1709 Minor refactoring to kill various Rubocop warnings 2021-12-17 20:29:47 +00:00
Maruno17
b5ee1b211d Replaced get_character(0) with get_self 2021-12-15 22:10:53 +00:00
Maruno17
eec29709ac Improved how followers go through and come out of doors 2021-12-15 22:01:53 +00:00
Nathan-mv
8a77c2d9e1 Updated Message (#143) 2021-12-14 23:24:45 +00:00
Maruno17
15b86429a2 Pokémon cries no longer change pitch except when fainting, added support for separate fainting cries 2021-12-14 23:19:08 +00:00
Maruno17
b706843888 Added $player.has_box_link and $player.has_exp_all in addition to those items 2021-12-14 22:33:46 +00:00
Maruno17
7d2e1027cd Updated TMs to BDSP 2021-12-14 22:15:32 +00:00
Nathan-mv
a89d552f8e Update Boxes (#144) 2021-12-13 22:24:46 +00:00
Maruno17
840e1a8be9 Updated Pokémon PBS files to BDSP 2021-12-13 22:09:21 +00:00
Maruno17
3da8b563da Changing encounter_version now updates the encounter tables immediately, title screen now plays properly if there are no splashes, compiling is now forced if any dat files are missing, other tweaks 2021-12-12 23:10:01 +00:00
Maruno17
2444b70ef6 Improved code for using multiples of an item at once, added a Setting for it, made it apply to more items 2021-12-12 00:10:01 +00:00
Maruno17
2ddedfb935 Tweaked code for interacting with a follower 2021-12-12 00:06:33 +00:00
Maruno17
85bf1a334c Fixed evolution-related code causing errors in Battle Frontier battles 2021-12-11 16:05:45 +00:00
Maruno17
c1695dd21b Fixed incorrect animation when a critical capture Poké Ball fails to catch the Pokémon 2021-12-11 15:46:07 +00:00
Maruno17
241851a75b Fixed crash when using upgradeRibbon, fixed crash when trading Pokémon, fixed crash when trying to play Triple Triad, fixed crash when generating Battle Frontier data 2021-12-11 13:25:23 +00:00
Maruno17
b9d69b780b Fixed bugs relating to followers from previous commit, fixed error in Safari Battles from previous commit 2021-12-11 01:44:44 +00:00
Maruno17
2e3d479752 Added def follower_animation which makes the next Show Animation event command apply to the follower instead 2021-12-09 23:42:06 +00:00
Maruno17
6a56a8f7d0 Added def follower_move_route which makes the next Move Route event command apply to the follower instead, events are no longer automatically erased if they are followers 2021-12-09 23:33:46 +00:00
Maruno17
c55f1f0a2a Follower checks when using some items/moves in the field are now more specific, which should help following Pokémon scripts 2021-12-09 23:30:52 +00:00
Maruno17
79e04bff6c Fixed code for happiness-based evolutions 2021-12-08 22:41:44 +00:00
Maruno17
054b4bc3e8 Corrosion now allows a Steel/Poison Pokémon to poison itself with Toxic Orb 2021-12-08 20:07:52 +00:00
Maruno17
514ef510f1 Added speed order viewer in battle debug menu, Pokemon#foreign? now defaults to comparing to the player 2021-12-07 23:28:14 +00:00
Maruno17
6b841b78d3 Added egg move sharing in Day Care, added some Settings for Day Care/Nursery mechanics, Neutralizing Gas now triggers held items if it negates Unnerve, Imposter now only triggers when the Pokémon enters battle 2021-12-07 22:48:15 +00:00
Maruno17
a858408951 Changed how Pokémon markings are recorded, and allowed variants of each mark (e.g. alternate colours) 2021-12-06 23:26:18 +00:00
Maruno17
5a81d447d1 Added Aura Wheel's visible type change in battle, added "display" methods for several move properties and examples of their use 2021-12-06 22:00:27 +00:00
Maruno17
95916e242e Added battle debug menu (access with F9) 2021-12-05 20:24:20 +00:00
Maruno17
3650a078e7 Fixed Toxic damage only going up to 15/16 of total HP, minor code fixes 2021-12-05 20:20:30 +00:00
Maruno17
5337ee1e97 Revert "Implemented Battle Debug Menu (#130)"
This reverts commit 7c48148d35.
2021-11-24 19:40:12 +00:00
Alexander Pahn
7c48148d35 Implemented Battle Debug Menu (#130)
* initial commit to setup game.ini
* renamed title of masters Game.ini
* reenabled gitignore for Game.ini
* Between Commit
* Finished Battle Debug Menu
* Clean up for pull request
* Manual fixes
* Fixed oversight where numerical min/max wasm't considered
2021-11-24 19:24:13 +00:00
Maruno17
479aeacc2c Added new section-based format for berry_plants.txt 2021-11-24 19:05:18 +00:00
Maruno17
00c2df5772 Merged species Type1/Type2 into Types, did the same for Pokemon and Battler 2021-11-22 23:55:28 +00:00
Maruno17
a5f91f62ea Made map names be added to map_metadata.txt when writing it if possible 2021-11-21 23:11:19 +00:00
Maruno17
d93d73caa8 Wrote a generalised data property editor that deals with a list of things from a GameData module, allowed the WildItem properties for species to contain multiple items each 2021-11-21 22:24:58 +00:00
Maruno17
c8b574ed7c Made Player Transfer event command not cancel surfing/diving, so pbTransferSurfing and pbTransferUnderwater are deprecated. 2021-11-21 19:10:16 +00:00
Maruno17
ddb95a8bb6 Added underscore to berry plant charsets, added Debug menu function that will rename these files (including in map data) 2021-11-21 18:45:13 +00:00
Maruno17
ea9cacd6b9 Updated console message code to Luka's latest version 2021-11-21 17:36:25 +00:00
Maruno17
b445f26a88 Converted Shadow Pokémon PBS file to a section-based format, improved Shadow Pokémon mechanics 2021-11-21 00:44:41 +00:00
Maruno17
048a18b415 Refactored ability/item/ball battle handlers, added "UltraBeast" species flag for Beast Ball 2021-11-18 22:52:19 +00:00
Maruno17
8307222009 Fix to previous commit 2021-11-17 21:54:02 +00:00
Maruno17
7ec8f30f0e Some battle method refactoring, fixed typo 2021-11-17 20:40:19 +00:00
Maruno17
1c4819e5f0 Renamed all battle-related classes and modules 2021-11-16 23:05:16 +00:00
Maruno17
6dacd6a139 Actual fix for berry plant harvesting crash 2021-11-15 22:34:02 +00:00
Maruno17
06493fecbb Removed more RMVX compatibility 2021-11-15 21:31:52 +00:00
Maruno17
99b6d86962 Berry plant bug fix from previous commit 2021-11-15 14:00:41 +00:00
Maruno17
f00553c1eb Bug fixes from previous commits 2021-11-15 08:38:39 +00:00
Maruno17
9657eca3d7 Fixed panoramas/fogs/other AnimatedPlanes not animating properly 2021-11-14 16:40:55 +00:00
Maruno17
a7f71e9620 Fixed bug in previous commits, fixed Frontier/Bug Contest battles not counting towards battle counts stats 2021-11-14 01:45:14 +00:00
Maruno17
46a92a33ef Added stat for cancelled evolutions 2021-11-13 23:16:22 +00:00
Maruno17
c6ecf60172 Added class GameStats, added Pokédex records for eggs seen and expanded seen_forms to include shinies 2021-11-13 23:13:28 +00:00
Maruno17
12fd500dbc Added Pokémon properties cannot_store, cannot_release and cannot_trade. Allowed Offspring species property to be edited. 2021-11-08 23:21:20 +00:00
Maruno17
15babcf835 Refining event evolution mechanics 2021-11-07 21:20:47 +00:00
Maruno17
34ab0b8afe Added evolution method for Galarian Yamask, minor refactoring in battle code, a Pokémon's ability no longer needs resetting when changing its ability_index 2021-11-07 18:05:32 +00:00
Maruno17
ae7721316f Re-added a way to erase (or change) a map's tiles 2021-11-03 22:40:02 +00:00
Maruno17
c392f8c236 Added Luka's console message colouring code 2021-11-02 23:27:42 +00:00
Maruno17
50d999e7da Updated to mkxp-z v2.2.3 (removed mp3 support) 2021-11-02 21:14:31 +00:00
Maruno17
5ad9f60d61 Added plugin meta.txt property "Essentials", which lists versions of Essentials the plugin is compatible with 2021-11-02 20:46:32 +00:00
Maruno17
cff6edac5b Fixed some bugs from recent commits 2021-11-02 18:37:48 +00:00
Maruno17
e8170a267c Fixed Exp Candies only raising happiness for one item if multiple are used at once 2021-10-31 20:54:53 +00:00
Maruno17
0ec67f78fa Added affection effects 2021-10-31 20:43:16 +00:00
Maruno17
ee16c22388 Added Hidden Power tutor compatibility to Gen 8 PBS files 2021-10-31 15:30:56 +00:00
Maruno17
67fc59ad4e Fixed typo from previous commit 2021-10-30 23:27:46 +01:00
Maruno17
308937a9f4 Refactored Day Care code 2021-10-30 21:56:34 +01:00
Maruno17
899d037255 Added pokemon.txt/pokemon_forms.txt property "Offspring" for species that could be produced by breeding 2021-10-29 20:34:50 +01:00
Maruno17
0c3ec24936 Fixed typo in previous commit 2021-10-27 23:54:04 +01:00
Maruno17
f3abcb7caf Obsoleted battle methods that yield battlers in favour of ones that return all of them at once 2021-10-27 22:45:34 +01:00
Maruno17
6066797517 Changing the player's character or outfit immediately updates the player's graphic again 2021-10-26 22:54:31 +01:00
Maruno17
6c38f769c7 Moved initial money/storage creator's name from Settings to metadata.txt, added initial item storage contents property to global metadata, added Home property to player metadata 2021-10-25 23:07:58 +01:00
Maruno17
680c1de392 AI now keeps their last defined Pokémon for last, tweaks to some battle effects based on mechanics generation 2021-10-24 23:06:47 +01:00
Maruno17
18049c22b9 More changes to console messages 2021-10-23 23:56:54 +01:00
Maruno17
e417e4c659 Tidied and coloured console messages 2021-10-23 21:55:42 +01:00
Maruno17
10a1fc4430 Added PBS file pokemon_metrics.txt, for all Pokémon sprite positionings 2021-10-23 20:30:09 +01:00
Maruno17
ca680c9feb Merged class PokemonTemp into class Game_Temp 2021-10-21 22:01:59 +01:00
Maruno17
5e51f702b3 Renamed some more global variables 2021-10-20 23:13:30 +01:00
Maruno17
e49cd8d498 Renamed $Trainer to $player 2021-10-20 22:57:43 +01:00
Maruno17
fbddb9034f Added a simple way to replace code in events with other code 2021-10-20 22:17:05 +01:00
Maruno17
3a3b44574f Various bug fixes 2021-10-20 13:53:39 +01:00
Maruno17
d3a07df6ab Fixed bug in previous commit 2021-10-19 23:57:00 +01:00
Maruno17
e7522321ad Fixed up code added by previous commit, improved scene closings when using Fly 2021-10-19 22:18:55 +01:00
Golisopod-User
e4e8e60d28 Added Flying from Town Map (#129)
* Added ability to fly from Town Map

* Refactored UI_RegionMap code
2021-10-19 20:03:22 +01:00
Maruno17
837208792a Fixed bug with Neutralizing Gas, fixed typo 2021-10-19 17:39:27 +01:00
Maruno17
aee0595d3f Fixed another crash when removing items from the Bag 2021-10-19 13:58:19 +01:00
Maruno17
50065754a0 Fixed crash when removing items from the Bag 2021-10-19 08:39:15 +01:00
Maruno17
f6a5ea86db Fixed coding error from previous commit 2021-10-17 23:19:06 +01:00
Maruno17
36ff7c4ba3 Renamed variables and methods relating to the Bag, renamed $PokemonBag to $Bag, $bag.has? can now check for a minimum quantity 2021-10-17 23:02:58 +01:00
Maruno17
72c50db6c0 Tidying up of Gen 8 ability code, fixed Analytic's effect, added trigger for berry consuming when Unnerve ends 2021-10-17 19:49:25 +01:00
Maruno17
c68e5e7abf Implemented Neutralizing Gas 2021-10-17 18:35:57 +01:00
Maruno17
56c9b69c44 Added "Name" property to map_metadata.txt, fixed map names not being translated in some cases, fixed minor things from a previous commit 2021-10-13 22:55:29 +01:00
Maruno17
a5bbd1fb8a Fixed map rendering glitching when using an oversized tileset 2021-10-12 19:05:05 +01:00
Maruno17
de0eb8e4e1 Fixed pbOnStepTaken being called every frame if you open a menu immediately at the end of a step, fixed Sweet Veil being defined incorrectly 2021-10-10 19:09:36 +01:00
Maruno17
0aad105c6d Made multiple special battle animations easier to register 2021-10-10 17:47:08 +01:00
Maruno17
45fd570414 Fixed Pledge combo moves sometimes using the wrong type, fixed command windows in battle being too tall if possible, added Debug menu feature to toggle credits skippability 2021-10-10 16:41:47 +01:00
Maruno17
a090f50bc5 Split metadata.txt into metadata.txt and map_metadata.txt, fixed bug when writing certain PBS files 2021-10-09 23:34:45 +01:00
Maruno17
166a289a60 Fixed transitions in controls and credits screens, fixed slight text cropping in credits screen, fixed crash when changing the scene 2021-10-07 18:47:28 +01:00
Maruno17
c8b594d38d Fixed crash when starting a new game 2021-10-06 00:00:12 +01:00
Maruno17
c0ac135a69 Added support for extra autotiles used instead of the top tiles of the tileset 2021-10-05 19:14:23 +01:00
Maruno17
8bb8c1149f Fixed bridge tiles not changing their priority when pbBridgeOn/pbBridgeOff are called 2021-10-05 18:14:10 +01:00
Maruno17
3cc6a1201f Fixed Pomeg Berry glitch 2021-10-03 23:13:29 +01:00
Maruno17
1f9ba94432 Fixed quote marks around LoseText in trainers.txt 2021-10-03 22:42:18 +01:00
Maruno17
929a039f04 Fixed Terrain Tag Editor's background disappearing when saving changes 2021-10-03 22:33:55 +01:00
Maruno17
559caf1046 Fixed crash when walking into really tall grass on a connected map 2021-10-03 22:30:17 +01:00
Maruno17
c7858de42a Refactored berry plant code, added missing berry plant PBS data and charsets, fixed berry sparkles sometimes showing on the wrong map 2021-10-03 20:19:28 +01:00
Maruno17
af23f1ecc4 Removed old map renderer, tidied up 2021-10-02 23:54:06 +01:00
Maruno17
348b847b68 Changed example follower from Brendan to May just because 2021-10-02 23:12:37 +01:00
Maruno17
11544da43f Fixed default transition animation not being FPS-agnostic 2021-10-02 22:59:14 +01:00
Maruno17
f35a51f975 Rewrote and refactored follower code, fixed follower glitchiness actoss connected maps, fixed follower glitchiness around bridges 2021-10-02 22:57:54 +01:00
Maruno17
0b656edffc Added support for an extended autotile format containing full-tile concave corners 2021-10-01 19:50:47 +01:00
Maruno17
c496fed620 Added new map renderer 2021-09-29 18:18:50 +01:00
Maruno17
75a39dbc42 Fixed typo from commit relating to refactoring switching code 2021-09-23 08:29:28 +01:00
Maruno17
096f13f451 The player's charset now only changes itself at the start of a step or the start of not moving. Added potential for running/jumping/stationary speeds/charsets for surfing/diving/cycling. Tweaked fishing animation. 2021-09-22 22:57:06 +01:00
Maruno17
694e567f3d Fixed overworld weather moving relative to the screen rather than the map 2021-09-20 23:25:12 +01:00
Maruno17
4edd06208a Moved Gen 8 item code to its proper places 2021-09-19 23:14:24 +01:00
Maruno17
d4abc6ef2b Added Eject Pack's effect 2021-09-19 23:04:29 +01:00
Maruno17
1fb3ff5408 Refactoring of code relating to switching, effect damage and effects that trigger after a move is used 2021-09-19 19:03:17 +01:00
Maruno17
f00f030825 Added effect of Pokémon Box Link 2021-09-12 21:08:46 +01:00
Maruno17
f9c6e142e5 Added effect of Blunder Policy 2021-09-12 20:13:43 +01:00
Maruno17
df7c033a9d Tweaked Fissure's function code, added Mirror Armor's effect 2021-09-10 20:40:45 +01:00
Maruno17
27be1cb330 Tidied up some function codes, added missing ones 2021-09-06 21:26:59 +01:00
Maruno17
c670c63bf5 Renamed all move function codes 2021-09-06 20:56:37 +01:00
Maruno17
c7fd147040 Added effect of Dragon Darts, fixed incorrect status icon being used in battle 2021-09-04 20:53:43 +01:00
Maruno17
423961e524 Fixed unique Galarian evolutions not being flagged as being able to pass their form on when breeding, fixed bred Sinistea sometimes not being phony form 2021-09-02 22:01:17 +01:00
Maruno17
885c1193e3 Added Flags property to ribbons 2021-09-02 21:09:18 +01:00
Maruno17
cef3d08917 Generalised form inheritance, making use of species flags 2021-09-02 21:05:53 +01:00
Maruno17
86cbcad382 Added Flags property to types, abilities, species and map metadata. Added LocationFlag evolution method. 2021-09-02 19:01:16 +01:00
Maruno17
cfbefceb00 Renamed skill code property in trainer_types.txt to SkillFlags, and allowed any number of them 2021-08-31 17:36:48 +01:00
Maruno17
171f1aade2 Fixed visual mistake in SnakeSquares transition animation 2021-08-31 16:54:14 +01:00
Maruno17
218307d993 Added Gen 8's shiny chance increase with number battled, fixed other shiny chance-boosting effects not working 2021-08-31 16:54:03 +01:00
Maruno17
3c88c897f0 Refactored checking whether a Pokémon/species is single gendered, tweaked console message. 2021-08-31 00:17:30 +01:00
Maruno17
0405497868 Healing Wish/Lunar Dance now linger until they will do something, as per Gen 8 2021-08-30 00:38:06 +01:00
Maruno17
c92bb2fb42 Pokémon in storage no longer recalculate their form 2021-08-30 00:10:31 +01:00
Maruno17
70ef588190 Merge branch 'master' into dev 2021-08-29 23:50:52 +01:00
Maruno17
a9426b0802 Fixed Sweet Scent not working 2021-08-29 23:49:43 +01:00
Maruno17
f411867c52 Updated all BaseExp values for species and forms 2021-08-29 20:48:43 +01:00
Maruno17
a31ac0d4af Merge branch 'master' into dev 2021-08-29 18:24:34 +01:00
Maruno17
6db668b449 Fixed HappinessMoveType, fixed particle effects on events not working 2021-08-29 18:20:52 +01:00
Maruno17
c39eca3975 Merge branch 'master' into dev 2021-08-24 13:46:03 +01:00
Maruno17
6551697b4f Fixed incorrect layering of sprites in battle, unfixed RMXP transitions 2021-08-24 13:36:52 +01:00
Maruno17
7318d304de Fixed battle transition animations after fix to default RMXP animation 2021-08-23 19:35:44 +01:00
Maruno17
8bb70a226e Tidied up semicolon use, refactored random dungeon generation code, fixed visual bug in Day Care debug screen 2021-08-22 23:18:34 +01:00
Golisopod-User
ecc5a040cd Refactoring and Bugfixes (#127)
* Replacing nil + compact! with delete, delete_at and delete_if wherever possible
* Fixed Terrain Tag editor not setting the correct terrain
* Fixed Text Cursor in Animation Editor not going right
2021-08-22 22:25:29 +01:00
Maruno17
e57e1f4a1e Merge branch 'master' into dev 2021-08-22 00:03:32 +01:00
Maruno17
e0e5cfebb7 Fixed Pokédex searches not considering the properties of alternate forms, fixed RMXP-style battle transitions not working, tweaked form-inheriting code in breeding 2021-08-22 00:00:51 +01:00
Maruno17
1b9e7400f9 Minor fixes to some move definitions 2021-08-21 22:47:25 +01:00
Maruno17
45fba56455 Turned fusion items into two separate items for the sake of them having different descriptions, fixed ability-changing items not doing so, added Zygarde Cube's Gen 8 effect 2021-08-21 18:26:48 +01:00
Maruno17
41ce9309d7 Refactored and improved transition animations 2021-08-20 21:39:20 +01:00
Maruno17
9c5c3c77ed Fixed changing a Pokémon's species to a single gendered/non-gendered one not updating its gender, enabled a bred Mr. Mime/Meowth to inherit Galarian forms 2021-08-19 18:29:53 +01:00
Maruno17
f768df4696 Added settings that enable disobedience check, swapped alternate movement speed key with pause menu key, fixed pbEventCommentInput not working, fixed issue with animating Exp bar upon hitting the max level 2021-08-15 18:27:37 +01:00
Maruno17
7ae62d74b7 Added terrain tag 17 NoEffect, Terrain Tag Editor now only sets defined terrain tags, fixed some move effects, fixed opposing Shadow Pokémon going into Hyper Mode, fixed Shadow Pokémon nature visibility 2021-08-14 22:36:55 +01:00
Maruno17
2112cdba37 Added effects of Mimicry/Room Service/Catching Charm, tweaked Sinistea's form chances, fixed bug in Fling about TRs. 2021-08-14 19:07:57 +01:00
Maruno17
153aa69bb8 Merge branch 'master' into dev 2021-08-12 20:37:25 +01:00
FL
4b768bec32 Fix event command "Return to Title Screen" (#124)
Fix loading a game after event command "Return to Title Screen" starting a loop.
2021-08-12 20:23:27 +01:00
Maruno17
19535b8160 Fixed Symbiosis, fixed Roost not roosting, fixed Normalize not boosting damage, fixed crash in Bug Catching Contest, fixed old format moves.txt compiling, added cache size text to console 2021-08-12 20:00:42 +01:00
Maruno17
c360c4ddd2 Added effects of Ripen/Gorilla Tactics/Steam Engine, fixed SE move-weakening berries working on normal effectiveness moves as well 2021-08-07 20:56:43 +01:00
Maruno17
61242a9836 Shortened ability/move descriptions to fit, added Howl's Gen 8 effect 2021-08-06 19:59:20 +01:00
Maruno17
2db3cc49af Merge branch 'master' into dev 2021-08-03 18:48:11 +01:00
Maruno17
7f86115c36 Fixed code relating to initial held items in battle, fixed default functionality of rand when no number is given 2021-08-03 18:45:39 +01:00
Maruno17
76eaadda61 Marged Gen 8 AI code into existing script file 2021-08-02 23:06:55 +01:00
Golisopod-User
c6da16409e AI and other Gen 8 Effects (#123)
* Added Rattled being triggered by Intimidate in Gen 8
* Added Rapid Spin speed boost effect
* Added basic AI for Gen 8
* Tweaked Leppa berry's effect
* Added Ability Patch's exemption for Zygarde
* Added Curse's Gen 8 targeting change
* Added Teleport's Gen 8 effect
* Added check for Choice items in Instruct and Dancer
* Added Quash's Gen 8 change to the order that multiple Quashed Pokémon move in

Co-authored-by: Maruno17 <serialcolour@hotmail.com>
2021-08-02 22:56:55 +01:00
Maruno17
ec84d581ad Merge branch 'master' into dev 2021-07-28 18:44:19 +01:00
Maruno17
c8b1b6251b Fix for Pokémon editor deleting a moveset move when "changing" which move it is to the same move, fixed typo in def addBackgroundOrColoredPlane. 2021-07-28 18:42:32 +01:00
Maruno17
34dd2a9fbd Rewrote random dungeon code to improve it and fix some bugs, rewrote Bag's def rearrange 2021-07-25 19:00:36 +01:00
Maruno17
d71a3d47e8 Added effects of Gulp Missile and Unseen Fist, fixed typo with Ice Face 2021-07-24 21:19:01 +01:00
Maruno17
a1cff9cc36 Changed the content of error messages raised by code in a Script event command 2021-07-23 20:06:06 +01:00
Maruno17
912631a8b1 Fix to previous commit's code 2021-07-23 08:32:11 +01:00
Maruno17
cb0220b751 Cleaned up evolution method definitions, rewrote the "Fill Bag" Debug feature to make it much faster, removed all instances of changing the game window's title 2021-07-22 22:46:43 +01:00
Maruno17
d21d3cb1fd Merge branch 'master' into dev 2021-07-22 18:36:38 +01:00
Maruno17
1bd5652a3a Fixed typo in Conversion's code that treated a type as an item 2021-07-22 18:35:30 +01:00
Maruno17
551f64e8a1 Added "Consumable" property to items.txt, light effects now properly centre themselves on the event regardless of graphic size, Disguise/Double Iron Bash Gen 8 changes 2021-07-21 22:46:49 +01:00
Maruno17
03a7ff39ad Merge branch 'master' into dev 2021-07-18 23:14:45 +01:00
Maruno17
4d9c8feb81 Fixed Poké Radar rustling grass not always causing a wild encounter when stepping in it, other tweaks to Poké Radar 2021-07-18 23:13:37 +01:00
Maruno17
8f72876a23 Typo fix 2021-07-17 21:17:13 +01:00
Maruno17
14748f4999 Added some more Gen 8 item effects, added Intimidate being blocked by some abilities, fixed Nectars being usable on fainted Oricorio 2021-07-17 20:48:06 +01:00
Maruno17
78f5530cbe Added more Gen 8 code 2021-07-13 20:52:26 +01:00
Maruno17
8b7a532e29 Updating some PBS files 2021-07-11 21:58:57 +01:00
Maruno17
3d634a7f22 Added separate SellPrice property for items, changed messages when learning a move, updates to Gen 8 items.txt 2021-07-10 22:17:02 +01:00
Maruno17
8ed9c5139e Merge branch 'dev' into dev-gen8 2021-07-10 19:49:08 +01:00
Maruno17
4b98850c3a Stopped U-turn switching if the battle is over, ensured animation spritesheet file extensions are removed from animation data, added Debug function to reload system cache, Pokémon debug menu now shows the Pokémon's status problem, changed mkxp.json to fix uncommon rendering problems 2021-07-10 19:23:56 +01:00
Maruno17
33f0403945 Updated happiness values (assuming Timburr/Stunfisk not changing are mistakes), added new type of evolution that triggers after any battle, added code to let Galarian Farfetch'd evolve after dealing 3 critical hits 2021-07-09 21:44:58 +01:00
Maruno17
ae7661edc5 Some fixes related to the previous commit and Exp Candy effect 2021-07-08 23:38:57 +01:00
Maruno17
d5aeeaca7b Added code for Gen 8 alternate forms 2021-07-08 23:04:21 +01:00
Maruno17
ba6806aa5b Added Gen 8 PBS files 2021-07-08 21:19:45 +01:00
Maruno17
633a0b1a1b Fixed typos 2021-07-01 22:30:16 +01:00
Maruno17
f4ba8951c6 Merge branch 'dev' into dev-gen8 2021-07-01 22:29:06 +01:00
Maruno17
16a11d314f Merge branch 'master' into dev 2021-07-01 22:26:48 +01:00
Maruno17
66ed71dcad Fixed error when trying to make a roaming Pokémon roam 2021-07-01 22:26:24 +01:00
Maruno17
fc54ed5630 Merge branch 'dev' into dev-gen8 2021-07-01 21:59:47 +01:00
Maruno17
987cb7640c Turned move flags into separate words 2021-07-01 21:54:22 +01:00
Maruno17
536a0b4f9a Applied code related to deprecated move flag for Magic Coat 2021-07-01 21:11:42 +01:00
Maruno17
0af3fe46bd Merge branch 'dev' into dev-gen8 2021-07-01 21:09:05 +01:00
Maruno17
93b67c9047 Deprecated three move flags as they should depend on move effects instead 2021-07-01 21:06:24 +01:00
Maruno17
5736ef404b Made various changes to effects, added some Gen 8 mechanics 2021-06-30 23:25:47 +01:00
Maruno17
d5459fb97c Merge branch 'dev' into dev-gen8 2021-06-28 19:53:12 +01:00
Maruno17
bc7f469308 Merge branch 'master' into dev 2021-06-28 19:52:43 +01:00
Maruno17
c457d59a0e Fixed error in code used by Pickup 2021-06-28 19:32:04 +01:00
Maruno17
d4c01724c7 Added Rare Candy being usable at level 100, added event evolutions 2021-06-27 18:32:43 +01:00
Maruno17
66169b56be Merge branch 'dev' into dev-gen8 2021-06-27 17:40:01 +01:00
Maruno17
753650acf3 Merge branch 'master' into dev 2021-06-27 17:39:23 +01:00
aprogergely
cb60fa1299 typo (#122)
.form should be lowercase to work
2021-06-27 17:37:35 +01:00
aprogergely
84b1512c9b typo (#121)
the ! should always be in front of the word, not after it
2021-06-27 17:37:23 +01:00
Maruno17
e93b08a87b Added some more Gen 8 move effects 2021-06-26 22:12:47 +01:00
Maruno17
18408d204e Merge branch 'dev' into dev-gen8 2021-06-24 20:59:00 +01:00
Maruno17
80e95dc48a Fixed slightly inaccurate chances for each number of hits for moves that hit 2-5 times 2021-06-24 20:57:32 +01:00
Maruno17
49e7374c3d Added more Gen 8 move effects 2021-06-24 20:47:47 +01:00
Maruno17
223d480e0f Super shininess can now be set for a Pokémon, and trainers.txt can define a Pokémon as super shiny 2021-06-24 18:21:47 +01:00
Maruno17
bb429a2680 Moved the Gen 8 items so that the game actually runs 2021-06-23 23:20:52 +01:00
Maruno17
feb9e3b2de Added some Gen 8 item effects, added super shininess 2021-06-23 23:15:15 +01:00
Maruno17
0ad86e6b03 Added some Gen 8 ability effects 2021-06-22 22:36:12 +01:00
Maruno17
9344ef8d04 Added setting that determines if Pokémon put into storage are healed 2021-06-21 23:28:51 +01:00
Maruno17
43900dca8c Added some Gen 8 move effects 2021-06-21 22:17:55 +01:00
Maruno17
9e1e113333 Added groundwork for Gen 8 code 2021-06-21 19:19:37 +01:00
Maruno17
eceb7f2084 Merge branch 'master' into dev 2021-06-20 23:12:30 +01:00
Golisopod-User
3c3802b80b Fixed Plugin Manager raising an error when optional dependency of Plugin is not installed (#119) 2021-06-20 23:11:18 +01:00
CorvusAtrox
657ed7944d Fix Crash on attempting to get Favored Types (#120) 2021-06-20 23:00:42 +01:00
Maruno17
c194e1b711 Changed the names of some properties in pokemon.txt and pokemon_forms.txt 2021-06-20 20:57:44 +01:00
Maruno17
565b15dca4 Renamed various PBS files 2021-06-20 20:27:57 +01:00
Maruno17
b1caf13110 Updating all PBS files to new formats 2021-06-20 18:45:55 +01:00
Maruno17
ca09e8c092 Removed all deprecated code slated for removal, removed support for pre-v19 save files 2021-06-20 18:37:37 +01:00
Maruno17
53d27d3cf5 Added new section-based format for ribbons.txt 2021-06-20 17:57:35 +01:00
Maruno17
e201821919 Added new section-based format for abilities.txt 2021-06-20 17:46:36 +01:00
Maruno17
7c42e4ec20 Added new section-based format for moves.txt 2021-06-20 17:29:16 +01:00
Maruno17
1a55a391a3 Removed support for old trainers.txt format 2021-06-20 01:06:49 +01:00
Maruno17
1cec4fc90d Added new section-based format for trainertypes.txt 2021-06-20 01:00:21 +01:00
Maruno17
e4cdb95314 Added new section-based format for items.txt 2021-06-20 00:13:34 +01:00
Maruno17
9b4bc66dc0 Tidying up 2021-06-19 20:36:17 +01:00
Maruno17
e472d0fcdf Rewrote PBS files to set all their "ID numbers" to 0 2021-06-19 20:18:55 +01:00
Maruno17
c55324145d Removed all other uses of and support for ID numbers 2021-06-19 20:01:12 +01:00
Maruno17
49655165e1 Added conversion of berry plant data, removed all uses of ID numbers for abilities and berry plants, fixed mulch not being consumed, removed use of ID numbers in map of moves to animation IDs 2021-06-19 18:48:33 +01:00
Maruno17
6e188666a4 Removed all uses of ID numbers for GameDatas BodyColor, BodyShape and Target, removed support for trainer type graphics using ID numbers in their names 2021-06-17 22:45:16 +01:00
Maruno17
eaa915878a Added def count to all GameData variants, and one that returns the number of species for Species, removed all uses of ID numbers for GameData::Status, made more use of GameData::X.keys 2021-06-17 22:21:24 +01:00
Maruno17
5358037986 Removed support for old encounters.txt format 2021-06-16 22:53:01 +01:00
Maruno17
e9457a3ea8 Removed all uses of ID numbers for species, some other code changes for abolishing ID numbers 2021-06-16 22:42:20 +01:00
Maruno17
8c67127f06 Removed all uses of ID numbers for types, Shape property in pokemon.txt must now be a name and not a number 2021-06-16 20:32:30 +01:00
Maruno17
0e8b1e70b1 Updated to mkxp-z 2.2.1 2021-06-15 22:11:12 +01:00
Maruno17
5df5e83f68 Removed dual functionalities of Ability and Ball properties in trainers.txt, abolished $BallTypes, removed support for tm.txt 2021-06-15 22:03:16 +01:00
Maruno17
9226bce078 Renamed some PBS files 2021-06-15 20:58:11 +01:00
691 changed files with 394038 additions and 155064 deletions

8
.gitignore vendored
View File

@@ -2,13 +2,16 @@
Audio/
Graphics/
Plugins/
Screenshots/
# Data folder, but not Data/Scripts folder
# Data folder, but not Data/Scripts folder or messages_core.dat
Data/*
!Data/Scripts.rxdata
!Data/Scripts/
!Data/messages_core.dat
# Files in the main project folder
errorlog.txt
Game.ini
Game.rxproj
RGSS*.dll
@@ -17,6 +20,7 @@ RGSS*.dll
.vscode/
*.code-workspace
.idea/
*.mkproj
# Operating system generated files & folders
.DS_Store
@@ -27,4 +31,4 @@ Thumbs.db
# Temp files
~*
~*

225
.rubocop.yml Normal file
View File

@@ -0,0 +1,225 @@
AllCops:
TargetRubyVersion: "3.0"
NewCops: enable
#===============================================================================
# Layout
#===============================================================================
# We don't need empty lines in methods to separate "return if"s from later code.
Layout/EmptyLineAfterGuardClause:
Enabled: false
# Extra whitespace often helps to make code more presentable.
Layout/ExtraSpacing:
AllowForAlignment: true
AllowBeforeTrailingComments: true
# Looks better than having hash elements shifted way to the right just to line
# up with the hash's opening bracket.
Layout/FirstHashElementIndentation:
EnforcedStyle: consistent
# In a hash with multiple values (one per line), prefer the => to be lined up
# and text to otherwise be left-aligned.
Layout/HashAlignment:
EnforcedHashRocketStyle: table
EnforcedColonStyle: table
# This interferes with the presentation of some code, notably registered procs.
Layout/MultilineMethodCallBraceLayout:
Enabled: false
# This means hashes and arrays are written the same way, rather than hashes
# needing to be written like { foo => bar } while arrays are like [foo, bar].
Layout/SpaceInsideHashLiteralBraces:
EnforcedStyle: no_space
#===============================================================================
# Lint
#===============================================================================
# Some methods and blocks will have unused arguments. That's fine.
Lint/UnusedBlockArgument:
Enabled: false
Lint/UnusedMethodArgument:
Enabled: false
#===============================================================================
# Metrics
#===============================================================================
# Yes, Essentials has classes/modules/methods that are too big and complex.
# That's just how it is.
Metrics:
Enabled: false
#===============================================================================
# Naming
#===============================================================================
# This cop forbids class/module names with underscores in them. Having
# underscores isn't the end of the world.
Naming/ClassAndModuleCamelCase:
Enabled: false
# Script files are given names that look reasonable in the list of script
# sections in RMXP, and are all numbered. They won't be camel_case.
Naming/FileName:
Enabled: false
# Disabled for sanity's sake. While this is a cop we want to obey, fixing all
# this is a gargantuan task that may never be completed, and we don't need
# rubocop telling us about the 4000+ instances of camelCase method names.
Naming/MethodName:
Enabled: false
# Disabled for sanity's sake. While this is a cop we want to obey, fixing all
# this is a gargantuan task that may never be completed, and we don't need
# rubocop telling us about the 1500+ instances of camelCase parameter names.
Naming/MethodParameterName:
Enabled: false
# Disabled for sanity's sake. While this is a cop we want to obey, fixing all
# this is a gargantuan task that may never be completed, and we don't need
# rubocop telling us about the 10000+ instances of camelCase variable names.
Naming/VariableName:
Enabled: false
#===============================================================================
# Security
#===============================================================================
# Script event conditions and script switches are eval'd, amongst other things.
Security/Eval:
Enabled: false
# Plenty of things are loaded via Marshal.
Security/MarshalLoad:
Enabled: false
#===============================================================================
# Style
#===============================================================================
# List the attr_reader/writer/accessor variables however you want.
Style/AccessorGrouping:
Enabled: false
# The assign_to_condition style looks awful, indenting loads of lines and
# increasing the separation between variable and value being assigned to it.
# Having said that, using "assign_inside_condition" flags every instance of
# conditional assignment using a one-line ternary operator, so this cop has been
# disabled because such assignment is fine.
Style/ConditionalAssignment:
Enabled: false
EnforcedStyle: assign_inside_condition
# Check with yard instead.
Style/Documentation:
Enabled: false
# This is just shorthand that looks bad due to the lack of an "end" to a "def".
Style/EndlessMethod:
EnforcedStyle: disallow
# It's a choice between format and sprintf. We already make use of sprintf and
# the translatable _ISPRINTF, so...
Style/FormatString:
EnforcedStyle: sprintf
# Prefer sprintf("%s", "Hello") over sprintf("%<greeting>s", greeting: "Hello")
# because it should be easy enough to see which token is which, and it saves
# space.
Style/FormatStringToken:
EnforcedStyle: unannotated
# String literals are not frozen by default, which makes this comment a
# pointless bit of boilerplate that we neither need nor want.
Style/FrozenStringLiteralComment:
Enabled: false
# RMXP and Essentials use lots of global variables.
Style/GlobalVars:
Enabled: false
# Mixing the styles within a hash just looks silly.
Style/HashSyntax:
EnforcedStyle: no_mixed_keys
# Sometimes you want to clearly separate sets of code, one per "paradigm".
Style/IfInsideElse:
Enabled: false
AllowIfModifier: true
# The alernative is ->(x) { x } which is less English than "lambda". This style
# makes lambda definitions require the word "lambda".
Style/Lambda:
EnforcedStyle: lambda
# unless just adds mental gymnastics trying to figure out what it actually
# means. I much prefer if !something.
Style/NegatedIf:
Enabled: false
# .zero?, .positive? and .negative? are more wordy than == 0, > 0 and < 0. They
# also aren't consistent with other value comparisons, e.g. x > 42.
Style/NumericPredicate:
EnforcedStyle: comparison
# Following this just means that calls to an affected method need to know what
# that method calls its parameters, which is ridiculous. Keep things short and
# simple.
Style/OptionalBooleanParameter:
Enabled: false
# has_key? and has_value? are far more readable than key? and value?
Style/PreferredHashMethods:
Enabled: false
# Explicit returns help to show whether a method returns a value.
Style/RedundantReturn:
Enabled: false
# Enforcing the names of variables? To single letter ones? Just no.
Style/SingleLineBlockParams:
Enabled: false
# Single line methods use up less space, and they're easier to list next to each
# other and see that they behave similarly.
Style/SingleLineMethods:
Enabled: false
# This requires writing array[n..] instead of array[n..-1], and I think endless
# ranges look bad.
Style/SlicingWithRange:
Enabled: false
# Single quotes being faster is hardly measurable and only affects parse time.
# Enforcing double quotes reduces the times where you need to change them
# when introducing an interpolation or an apostrophe. Use single quotes only if
# their semantics are needed.
Style/StringLiterals:
EnforcedStyle: double_quotes
# This cop requires arrays of symbols/text to be written like %i[a b c]. We
# don't need that nonsense. ["a", "b", "c"] is clearer and introduces no
# additional syntax to confuse people.
Style/SymbolArray:
EnforcedStyle: brackets
Style/WordArray:
EnforcedStyle: brackets
# Allows procs to be written like { |obj| obj.something } which is clearer.
Style/SymbolProc:
AllowMethodsWithArguments: true
# Parentheses around the condition in a ternary operator helps to differentiate
# it from the true/false results.
Style/TernaryParentheses:
EnforcedStyle: require_parentheses
# This prefers "x += 1 while x < 10" and "x += 1 until x == 10". This hides
# loops, which is not good.
Style/WhileUntilModifier:
Enabled: false

Binary file not shown.

View File

@@ -1,12 +1,12 @@
#==============================================================================#
# Pokémon Essentials #
# Version 19.1.dev #
# Version 21.1 #
# https://github.com/Maruno17/pokemon-essentials #
#==============================================================================#
module Settings
# The version of your game. It has to adhere to the MAJOR.MINOR.PATCH format.
GAME_VERSION = '1.0.0'
GAME_VERSION = "1.0.0"
# The generation that the battle system follows. Used throughout the battle
# scripts, and also by some other settings which are used in and out of battle
@@ -14,40 +14,39 @@ module Settings
# Note that this isn't perfect. Essentials doesn't accurately replicate every
# single generation's mechanics. It's considered to be good enough. Only
# generations 5 and later are reasonably supported.
MECHANICS_GENERATION = 7
MECHANICS_GENERATION = 9
#=============================================================================
#-----------------------------------------------------------------------------
# Credits
#-----------------------------------------------------------------------------
# The default screen width (at a scale of 1.0).
SCREEN_WIDTH = 512
# The default screen height (at a scale of 1.0).
SCREEN_HEIGHT = 384
# The default screen scale factor. Possible values are 0.5, 1.0, 1.5 and 2.0.
SCREEN_SCALE = 1.0
# Your game's credits, in an array. You can allow certain lines to be
# translated by wrapping them in _INTL() as shown. Blank lines are just "".
# To split a line into two columns, put "<s>" in it. Plugin credits and
# Essentials engine credits are added to the end of these credits
# automatically.
def self.game_credits
return [
_INTL("My Game by:"),
"Maruno",
"",
_INTL("Also involved were:"),
"A. Lee Uss<s>Anne O'Nymus",
"Ecksam Pell<s>Jane Doe",
"Joe Dan<s>Nick Nayme",
"Sue Donnim<s>",
"",
_INTL("Special thanks to:"),
"Pizza"
]
end
#=============================================================================
#-----------------------------------------------------------------------------
# The player and NPCs
#-----------------------------------------------------------------------------
# The maximum level Pokémon can reach.
MAXIMUM_LEVEL = 100
# The level of newly hatched Pokémon.
EGG_LEVEL = 1
# The odds of a newly generated Pokémon being shiny (out of 65536).
SHINY_POKEMON_CHANCE = (MECHANICS_GENERATION >= 6) ? 16 : 8
# The odds of a wild Pokémon/bred egg having Pokérus (out of 65536).
POKERUS_CHANCE = 3
# Whether a bred baby Pokémon can inherit any TM/HM moves from its father. It
# can never inherit TM/HM moves from its mother.
BREEDING_CAN_INHERIT_MACHINE_MOVES = (MECHANICS_GENERATION <= 5)
# Whether a bred baby Pokémon can inherit egg moves from its mother. It can
# always inherit egg moves from its father.
BREEDING_CAN_INHERIT_EGG_MOVES_FROM_MOTHER = (MECHANICS_GENERATION >= 6)
#=============================================================================
# The amount of money the player starts the game with.
INITIAL_MONEY = 3000
# The maximum amount of money the player can have.
MAX_MONEY = 999_999
MAX_MONEY = 9_999_999
# The maximum number of Game Corner coins the player can have.
MAX_COINS = 99_999
# The maximum number of Battle Points the player can have.
@@ -55,65 +54,56 @@ module Settings
# The maximum amount of soot the player can have.
MAX_SOOT = 9_999
# The maximum length, in characters, that the player's name can be.
MAX_PLAYER_NAME_SIZE = 10
# The maximum number of Pokémon that can be in the party.
MAX_PARTY_SIZE = 6
#=============================================================================
# A set of arrays each containing a trainer type followed by a Global Variable
# number. If the variable isn't set to 0, then all trainers with the
# associated trainer type will be named as whatever is in that variable.
MAX_PLAYER_NAME_SIZE = 12
# A set of arrays each containing a trainer type followed by a Game Variable
# number. If the Variable isn't set to 0, then all trainers with the
# associated trainer type will be named as whatever is in that Variable.
RIVAL_NAMES = [
[:RIVAL1, 12],
[:RIVAL2, 12],
[:CHAMPION, 12]
]
#=============================================================================
#-----------------------------------------------------------------------------
# Overworld
#-----------------------------------------------------------------------------
# Whether outdoor maps should be shaded according to the time of day.
TIME_SHADING = true
#=============================================================================
# Whether poisoned Pokémon will lose HP while walking around in the field.
POISON_IN_FIELD = (MECHANICS_GENERATION <= 4)
# Whether poisoned Pokémon will faint while walking around in the field
# (true), or survive the poisoning with 1 HP (false).
POISON_FAINT_IN_FIELD = (MECHANICS_GENERATION <= 3)
TIME_SHADING = true
# Whether the reflections of the player/events will ripple horizontally.
ANIMATE_REFLECTIONS = true
# Whether planted berries grow according to Gen 4 mechanics (true) or Gen 3
# mechanics (false).
NEW_BERRY_PLANTS = (MECHANICS_GENERATION >= 4)
NEW_BERRY_PLANTS = (MECHANICS_GENERATION >= 4)
# Whether fishing automatically hooks the Pokémon (true), or whether there is
# a reaction test first (false).
FISHING_AUTO_HOOK = false
FISHING_AUTO_HOOK = false
# The ID of the common event that runs when the player starts fishing (runs
# instead of showing the casting animation).
FISHING_BEGIN_COMMON_EVENT = -1
# The ID of the common event that runs when the player stops fishing (runs
# instead of showing the reeling in animation).
FISHING_END_COMMON_EVENT = -1
#=============================================================================
# The number of steps allowed before a Safari Zone game is over (0=infinite).
SAFARI_STEPS = 600
# The number of seconds a Bug Catching Contest lasts for (0=infinite).
BUG_CONTEST_TIME = 20 * 60 # 20 minutes
#=============================================================================
# Pairs of map IDs, where the location signpost isn't shown when moving from
# one of the maps in a pair to the other (and vice versa). Useful for single
# long routes/towns that are spread over multiple maps.
SAFARI_STEPS = 600
# The number of seconds a Bug-Catching Contest lasts for (0=infinite).
BUG_CONTEST_TIME = 20 * 60 # 20 minutes
# Pairs of map IDs, where the location sign isn't shown when moving from one
# of the maps in a pair to the other (and vice versa). Useful for single long
# routes/towns that are spread over multiple maps.
# e.g. [4,5,16,17,42,43] will be map pairs 4,5 and 16,17 and 42,43.
# Moving between two maps that have the exact same name won't show the
# location signpost anyway, so you don't need to list those maps here.
NO_SIGNPOSTS = []
#=============================================================================
# location sign anyway, so you don't need to list those maps here.
NO_LOCATION_SIGNS = []
# Whether poisoned Pokémon will lose HP while walking around in the field.
POISON_IN_FIELD = (MECHANICS_GENERATION <= 4)
# Whether poisoned Pokémon will faint while walking around in the field
# (true), or survive the poisoning with 1 HP (false).
POISON_FAINT_IN_FIELD = (MECHANICS_GENERATION <= 3)
#-----------------------------------------------------------------------------
# Using moves in the overworld
#-----------------------------------------------------------------------------
# Whether you need at least a certain number of badges to use some hidden
# moves in the field (true), or whether you need one specific badge to use
# them (false). The amounts/specific badges are defined below.
@@ -133,105 +123,54 @@ module Settings
BADGE_FOR_DIVE = 7
BADGE_FOR_WATERFALL = 8
#=============================================================================
#-----------------------------------------------------------------------------
# Pokémon
#-----------------------------------------------------------------------------
# If a move taught by a TM/HM/TR replaces another move, this setting is
# whether the machine's move retains the replaced move's PP (true), or whether
# the machine's move has full PP (false).
TAUGHT_MACHINES_KEEP_OLD_PP = (MECHANICS_GENERATION == 5)
# Whether the Black/White Flutes will raise/lower the levels of wild Pokémon
# respectively (true), or will lower/raise the wild encounter rate
# respectively (false).
FLUTES_CHANGE_WILD_ENCOUNTER_LEVELS = (MECHANICS_GENERATION >= 6)
# Whether Repel uses the level of the first Pokémon in the party regardless of
# its HP (true), or it uses the level of the first unfainted Pokémon (false).
REPEL_COUNTS_FAINTED_POKEMON = (MECHANICS_GENERATION >= 6)
# Whether Rage Candy Bar acts as a Full Heal (true) or a Potion (false).
RAGE_CANDY_BAR_CURES_STATUS_PROBLEMS = (MECHANICS_GENERATION >= 7)
# The maximum level Pokémon can reach.
MAXIMUM_LEVEL = 100
# The level of newly hatched Pokémon.
EGG_LEVEL = 1
# The odds of a newly generated Pokémon being shiny (out of 65536).
SHINY_POKEMON_CHANCE = (MECHANICS_GENERATION >= 6) ? 16 : 8
# Whether super shininess is enabled (uses a different shiny animation).
SUPER_SHINY = (MECHANICS_GENERATION == 8)
# Whether Pokémon with the "Legendary", "Mythical" or "Ultra Beast" flags will
# have at least 3 perfect IVs.
LEGENDARIES_HAVE_SOME_PERFECT_IVS = (MECHANICS_GENERATION >= 6)
# The odds of a wild Pokémon/bred egg having Pokérus (out of 65536).
POKERUS_CHANCE = 3
# Whether IVs and EVs are treated as 0 when calculating a Pokémon's stats.
# IVs and EVs still exist, and are used by Hidden Power and some cosmetic
# things as normal.
DISABLE_IVS_AND_EVS = false
# Whether the Move Relearner can also teach egg moves that the Pokémon knew
# when it hatched and moves that the Pokémon was once taught by a TR. Moves
# from the Pokémon's level-up moveset of the same or a lower level than the
# Pokémon can always be relearned.
MOVE_RELEARNER_CAN_TEACH_MORE_MOVES = (MECHANICS_GENERATION >= 6)
#=============================================================================
#-----------------------------------------------------------------------------
# Breeding Pokémon and Day Care
#-----------------------------------------------------------------------------
# The name of the person who created the Pokémon storage system.
def self.storage_creator_name
return _INTL("Bill")
end
# The number of boxes in Pokémon storage.
NUM_STORAGE_BOXES = 30
# Whether Pokémon in the Day Care gain Exp for each step the player takes.
# This should be true for the Day Care and false for the Pokémon Nursery, both
# of which use the same code in Essentials.
DAY_CARE_POKEMON_GAIN_EXP_FROM_WALKING = (MECHANICS_GENERATION <= 6)
# Whether two Pokémon in the Day Care can learn egg moves from each other if
# they are the same species.
DAY_CARE_POKEMON_CAN_SHARE_EGG_MOVES = (MECHANICS_GENERATION >= 8)
# Whether a bred baby Pokémon can inherit any TM/TR/HM moves from its father.
# It can never inherit TM/TR/HM moves from its mother.
BREEDING_CAN_INHERIT_MACHINE_MOVES = (MECHANICS_GENERATION <= 5)
# Whether a bred baby Pokémon can inherit egg moves from its mother. It can
# always inherit egg moves from its father.
BREEDING_CAN_INHERIT_EGG_MOVES_FROM_MOTHER = (MECHANICS_GENERATION >= 6)
#=============================================================================
# The names of each pocket of the Bag. Ignore the first entry ("").
def self.bag_pocket_names
return ["",
_INTL("Items"),
_INTL("Medicine"),
_INTL("Poké Balls"),
_INTL("TMs & HMs"),
_INTL("Berries"),
_INTL("Mail"),
_INTL("Battle Items"),
_INTL("Key Items")
]
end
# The maximum number of slots per pocket (-1 means infinite number). Ignore
# the first number (0).
BAG_MAX_POCKET_SIZE = [0, -1, -1, -1, -1, -1, -1, -1, -1]
# The maximum number of items each slot in the Bag can hold.
BAG_MAX_PER_SLOT = 999
# Whether each pocket in turn auto-sorts itself by item ID number. Ignore the
# first entry (the 0).
BAG_POCKET_AUTO_SORT = [0, false, false, false, true, true, false, false, false]
#=============================================================================
# Whether the Pokédex list shown is the one for the player's current region
# (true), or whether a menu pops up for the player to manually choose which
# Dex list to view if more than one is available (false).
USE_CURRENT_REGION_DEX = false
# The names of the Pokédex lists, in the order they are defined in the PBS
# file "regionaldexes.txt". The last name is for the National Dex and is added
# onto the end of this array (remember that you don't need to use it). This
# array's order is also the order of $Trainer.pokedex.unlocked_dexes, which
# records which Dexes have been unlocked (the first is unlocked by default).
# If an entry is just a name, then the region map shown in the Area page while
# viewing that Dex list will be the region map of the region the player is
# currently in. The National Dex entry should always behave like this.
# If an entry is of the form [name, number], then the number is a region
# number. That region's map will appear in the Area page while viewing that
# Dex list, no matter which region the player is currently in.
def self.pokedex_names
return [
[_INTL("Kanto Pokédex"), 0],
[_INTL("Johto Pokédex"), 1],
_INTL("National Pokédex")
]
end
# Whether all forms of a given species will be immediately available to view
# in the Pokédex so long as that species has been seen at all (true), or
# whether each form needs to be seen specifically before that form appears in
# the Pokédex (false).
DEX_SHOWS_ALL_FORMS = false
# An array of numbers, where each number is that of a Dex list (in the same
# order as above, except the National Dex is -1). All Dex lists included here
# will begin their numbering at 0 rather than 1 (e.g. Victini in Unova's Dex).
DEXES_WITH_OFFSETS = []
#=============================================================================
# A set of arrays, each containing details of a graphic to be shown on the
# region map if appropriate. The values for each array are as follows:
# * Region number.
# * Game Switch; the graphic is shown if this is ON (non-wall maps only).
# * X coordinate of the graphic on the map, in squares.
# * Y coordinate of the graphic on the map, in squares.
# * Name of the graphic, found in the Graphics/Pictures folder.
# * The graphic will always (true) or never (false) be shown on a wall map.
REGION_MAP_EXTRAS = [
[0, 51, 16, 15, "mapHiddenBerth", false],
[0, 52, 20, 14, "mapHiddenFaraday", false]
]
#=============================================================================
#-----------------------------------------------------------------------------
# Roaming Pokémon
#-----------------------------------------------------------------------------
# A list of maps used by roaming Pokémon. Each map has an array of other maps
# it can lead to.
@@ -247,45 +186,198 @@ module Settings
66 => [5, 21, 28, 31, 39, 41, 44, 47, 69],
69 => [5, 21, 28, 31, 39, 41, 44, 47, 66 ]
}
# A set of arrays, each containing the details of a roaming Pokémon. The
# information within each array is as follows:
# * Species.
# * Level.
# * Game Switch; the Pokémon roams while this is ON.
# * Encounter type (0=any, 1=grass/walking in cave, 2=surfing, 3=fishing,
# 4=surfing/fishing). See the bottom of PField_RoamingPokemon for lists.
# * Name of BGM to play for that encounter (optional).
# * Roaming areas specifically for this Pokémon (optional).
# A set of hashes, each containing the details of a roaming Pokémon. The
# information within each hash is as follows:
# * :species
# * :level
# * :icon - Filename in Graphics/UI/Town Map/ of the roamer's Town Map icon.
# * :game_switch - The Pokémon roams if this is nil or <=0 or if that Game
# Switch is ON. Optional.
# * :encounter_type - One of:
# :all = grass, walking in cave, surfing (default)
# :land = grass, walking in cave
# :water = surfing, fishing
# :surfing = surfing
# :fishing = fishing
# * :bgm - The BGM to play for the encounter. Optional.
# * :areas - A hash of map IDs that determine where this Pokémon roams. Used
# instead of ROAMING_AREAS above. Optional.
ROAMING_SPECIES = [
[:LATIAS, 30, 53, 0, "Battle roaming"],
[:LATIOS, 30, 53, 0, "Battle roaming"],
[:KYOGRE, 40, 54, 2, nil, {
2 => [ 21, 31 ],
21 => [2, 31, 69],
31 => [2, 21, 69],
69 => [ 21, 31 ]
}],
[:ENTEI, 40, 55, 1, nil]
# {
# :species => :LATIAS,
# :level => 30,
# :icon => "pin_latias",
# :game_switch => 53,
# :encounter_type => :all,
# :bgm => "Battle roaming"
# },
# {
# :species => :LATIOS,
# :level => 30,
# :icon => "pin_latios",
# :game_switch => 53,
# :encounter_type => :all,
# :bgm => "Battle roaming"
# },
# {
# :species => :KYOGRE,
# :level => 40,
# :game_switch => 54,
# :encounter_type => :surfing,
# :areas => {
# 2 => [ 21, 31 ],
# 21 => [2, 31, 69],
# 31 => [2, 21, 69],
# 69 => [ 21, 31 ]
# }
# },
# {
# :species => :ENTEI,
# :level => 40,
# :icon => "pin_entei",
# :game_switch => 55,
# :encounter_type => :land
# }
]
#=============================================================================
#-----------------------------------------------------------------------------
# Party and Pokémon storage
#-----------------------------------------------------------------------------
# A set of arrays, each containing the details of a wild encounter that can
# only occur via using the Poké Radar. The information within each array is as
# follows:
# * Map ID on which this encounter can occur.
# * Probability that this encounter will occur (as a percentage).
# * Species.
# * Minimum possible level.
# * Maximum possible level (optional).
POKE_RADAR_ENCOUNTERS = [
[5, 20, :STARLY, 12, 15],
[21, 10, :STANTLER, 14],
[28, 20, :BUTTERFREE, 15, 18],
[28, 20, :BEEDRILL, 15, 18]
# The maximum number of Pokémon that can be in the party.
MAX_PARTY_SIZE = 6
# The number of boxes in Pokémon storage.
NUM_STORAGE_BOXES = 40
# Whether putting a Pokémon into Pokémon storage will heal it. If false, they
# are healed by the Recover All: Entire Party event command (at Poké Centers).
HEAL_STORED_POKEMON = (MECHANICS_GENERATION <= 7)
#-----------------------------------------------------------------------------
# Items
#-----------------------------------------------------------------------------
# Whether various HP-healing items heal the amounts they do in Gen 7+ (true)
# or in earlier Generations (false).
REBALANCED_HEALING_ITEM_AMOUNTS = (MECHANICS_GENERATION >= 7)
# Whether vitamins can add EVs no matter how many that stat already has in it
# (true), or whether they can't make that stat's EVs greater than 100 (false).
NO_VITAMIN_EV_CAP = (MECHANICS_GENERATION >= 8)
# Whether Rage Candy Bar acts as a Full Heal (true) or a Potion (false).
RAGE_CANDY_BAR_CURES_STATUS_PROBLEMS = (MECHANICS_GENERATION >= 7)
# Whether the Black/White Flutes will raise/lower the levels of wild Pokémon
# respectively (true), or will lower/raise the wild encounter rate
# respectively (false).
FLUTES_CHANGE_WILD_ENCOUNTER_LEVELS = (MECHANICS_GENERATION >= 6)
# Whether Rare Candy can be used on a Pokémon that is already at its maximum
# level if it is able to evolve by level-up (if so, triggers that evolution).
RARE_CANDY_USABLE_AT_MAX_LEVEL = (MECHANICS_GENERATION >= 8)
# Whether the player can choose how many of an item to use at once on a
# Pokémon. This applies to Exp-changing items (Rare Candy, Exp Candies) and
# EV-changing items (vitamins, feathers, EV-lowering berries).
USE_MULTIPLE_STAT_ITEMS_AT_ONCE = (MECHANICS_GENERATION >= 8)
# If a move taught by a TM/HM/TR replaces another move, this setting is
# whether the machine's move retains the replaced move's PP (true), or whether
# the machine's move has full PP (false).
TAUGHT_MACHINES_KEEP_OLD_PP = (MECHANICS_GENERATION == 5)
# Whether you get 1 Premier Ball for every 10 of any kind of Poké Ball bought
# from a Mart at once (true), or 1 Premier Ball for buying 10+ regular Poké
# Balls (false).
MORE_BONUS_PREMIER_BALLS = (MECHANICS_GENERATION >= 8)
# The default sell price of an item to a Poké Mart is its buy price divided by
# this number.
ITEM_SELL_PRICE_DIVISOR = (MECHANICS_GENERATION >= 9) ? 4 : 2
#-----------------------------------------------------------------------------
# Pokédex
#-----------------------------------------------------------------------------
# The names of the Pokédex lists, in the order they are defined in the PBS
# file "regional_dexes.txt". The last name is for the National Dex and is
# added onto the end of this array.
# Each entry is either just a name, or is an array containing a name and a
# number. If there is a number, it is a region number as defined in
# town_map.txt. If there is no number, the number of the region the player is
# currently in will be used. The region number determines which Town Map is
# shown in the Area page when viewing that Pokédex list.
def self.pokedex_names
return [
[_INTL("Kanto Pokédex"), 0],
[_INTL("Johto Pokédex"), 1],
_INTL("National Pokédex")
]
end
# Whether the Pokédex list shown is the one for the player's current region
# (true), or whether a menu pops up for the player to manually choose which
# Dex list to view if more than one is available (false).
USE_CURRENT_REGION_DEX = false
# Whether all forms of a given species will be immediately available to view
# in the Pokédex so long as that species has been seen at all (true), or
# whether each form needs to be seen specifically before that form appears in
# the Pokédex (false).
DEX_SHOWS_ALL_FORMS = false
# An array of numbers, where each number is that of a Dex list (in the same
# order as above, except the National Dex is -1). All Dex lists included here
# will begin their numbering at 0 rather than 1 (e.g. Victini in Unova's Dex).
DEXES_WITH_OFFSETS = []
# Whether the Pokédex entry of a newly owned species will be shown after it
# hatches from an egg, after it evolves and after obtaining it from a trade,
# in addition to after catching it in battle.
SHOW_NEW_SPECIES_POKEDEX_ENTRY_MORE_OFTEN = (MECHANICS_GENERATION >= 7)
#-----------------------------------------------------------------------------
# Town Map
#-----------------------------------------------------------------------------
# A set of arrays, each containing details of a graphic to be shown on the
# region map if appropriate. The values for each array are as follows:
# * Region number.
# * Game Switch; the graphic is shown if this is ON (non-wall maps only).
# * X coordinate of the graphic on the map, in squares.
# * Y coordinate of the graphic on the map, in squares.
# * Name of the graphic, found in the Graphics/UI/Town Map folder.
# * The graphic will always (true) or never (false) be shown on a wall map.
REGION_MAP_EXTRAS = [
[0, 51, 16, 15, "hidden_Berth", false],
[0, 52, 20, 14, "hidden_Faraday", false]
]
# Whether the player can use Fly while looking at the Town Map. This is only
# allowed if the player can use Fly normally.
CAN_FLY_FROM_TOWN_MAP = true
#=============================================================================
#-----------------------------------------------------------------------------
# Phone
#-----------------------------------------------------------------------------
# The default setting for Phone.rematches_enabled, which determines whether
# trainers registered in the Phone can become ready for a rematch. If false,
# Phone.rematches_enabled = true will enable rematches at any point you want.
PHONE_REMATCHES_POSSIBLE_FROM_BEGINNING = false
# Whether the messages in a phone call with a trainer are colored blue or red
# depending on that trainer's gender. Note that this doesn't apply to contacts
# whose phone calls are in a Common Event; they will need to be colored
# manually in their Common Events.
COLOR_PHONE_CALL_MESSAGES_BY_CONTACT_GENDER = true
#-----------------------------------------------------------------------------
# Battle starting
#-----------------------------------------------------------------------------
# Whether Repel uses the level of the first Pokémon in the party regardless of
# its HP (true), or it uses the level of the first unfainted Pokémon (false).
REPEL_COUNTS_FAINTED_POKEMON = (MECHANICS_GENERATION >= 6)
# Whether more abilities affect whether wild Pokémon appear, which Pokémon
# they are, etc.
MORE_ABILITIES_AFFECT_WILD_ENCOUNTERS = (MECHANICS_GENERATION >= 8)
# Whether shiny wild Pokémon are more likely to appear if the player has
# previously defeated/caught lots of other Pokémon of the same species.
HIGHER_SHINY_CHANCES_WITH_NUMBER_BATTLED = (MECHANICS_GENERATION >= 8)
# Whether overworld weather can set the default terrain effect in battle.
# Storm weather sets Electric Terrain, and fog weather sets Misty Terrain.
OVERWORLD_WEATHER_SETS_BATTLE_TERRAIN = (MECHANICS_GENERATION >= 8)
#-----------------------------------------------------------------------------
# Game Switches
#-----------------------------------------------------------------------------
# The Game Switch that is set to ON when the player blacks out.
STARTING_OVER_SWITCH = 1
@@ -297,14 +389,23 @@ module Settings
# The Game Switch which, while ON, makes all Pokémon created considered to be
# met via a fateful encounter.
FATEFUL_ENCOUNTER_SWITCH = 32
# The Game Switch which, while ON, disables the effect of the Pokémon Box Link
# and prevents the player from accessing Pokémon storage via the party screen
# with it.
DISABLE_BOX_LINK_SWITCH = 35
#=============================================================================
#-----------------------------------------------------------------------------
# Overworld animation IDs
#-----------------------------------------------------------------------------
# ID of the animation played when the player steps on grass (grass rustling).
GRASS_ANIMATION_ID = 1
# ID of the animation played when the player lands on the ground after hopping
# over a ledge (shows a dust impact).
DUST_ANIMATION_ID = 2
# ID of the animation played when the player finishes taking a step onto still
# water (shows a water ripple).
WATER_RIPPLE_ANIMATION_ID = 8
# ID of the animation played when a trainer notices the player (an exclamation
# bubble).
EXCLAMATION_ANIMATION_ID = 3
@@ -321,17 +422,46 @@ module Settings
# is on the map (for new plant growth mechanics only).
PLANT_SPARKLE_ANIMATION_ID = 7
#=============================================================================
#-----------------------------------------------------------------------------
# Files
#-----------------------------------------------------------------------------
# An array of available languages in the game, and their corresponding message
# file in the Data folder. Edit only if you have 2 or more languages to choose
# from.
DEFAULT_WILD_BATTLE_BGM = "Battle wild"
DEFAULT_WILD_VICTORY_BGM = "Battle victory"
DEFAULT_WILD_CAPTURE_ME = "Battle capture success"
DEFAULT_TRAINER_BATTLE_BGM = "Battle trainer"
DEFAULT_TRAINER_VICTORY_BGM = "Battle victory"
#-----------------------------------------------------------------------------
# Languages
#-----------------------------------------------------------------------------
# An array of available languages in the game. Each one is an array containing
# the display name of the language in-game, and that language's filename
# fragment. A language will use the language data files from the Data folder
# called messages_FRAGMENT_core.dat and messages_FRAGMENT_game.dat (if they
# exist).
LANGUAGES = [
# ["English", "english.dat"],
# ["Deutsch", "deutsch.dat"]
# ["English", "english"],
# ["Deutsch", "deutsch"]
]
#=============================================================================
#-----------------------------------------------------------------------------
# Screen size and zoom
#-----------------------------------------------------------------------------
# The default screen width (at a scale of 1.0). You should also edit the
# property "defScreenW" in mkxp.json to match.
SCREEN_WIDTH = 512
# The default screen height (at a scale of 1.0). You should also edit the
# property "defScreenH" in mkxp.json to match.
SCREEN_HEIGHT = 384
# The default screen scale factor. Possible values are 0.5, 1.0, 1.5 and 2.0.
SCREEN_SCALE = 1.0
#-----------------------------------------------------------------------------
# Messages
#-----------------------------------------------------------------------------
# Available speech frames. These are graphic files in "Graphics/Windowskins/".
SPEECH_WINDOWSKINS = [
@@ -357,7 +487,6 @@ module Settings
"speech hgss 20",
"speech pl 18"
]
# Available menu frames. These are graphic files in "Graphics/Windowskins/".
MENU_WINDOWSKINS = [
"choice 1",
@@ -389,10 +518,30 @@ module Settings
"choice 27",
"choice 28"
]
#-----------------------------------------------------------------------------
# Debug helpers
#-----------------------------------------------------------------------------
# Whether the game will ask you if you want to fully compile every time you
# start the game (in Debug mode). You will not need to hold Ctrl/Shift to
# compile anything.
PROMPT_TO_COMPILE = false
# Whether the game will skip the intro splash screens and title screen, and go
# straight to the Continue/New Game screen. Only applies to playing in Debug
# mode.
SKIP_TITLE_SCREEN = true
# Whether the game will skip the Continue/New Game screen and go straight into
# a saved game (if there is one) or start a new game (if there isn't). Only
# applies to playing in Debug mode.
SKIP_CONTINUE_SCREEN = false
end
#===============================================================================
# DO NOT EDIT THESE!
#===============================================================================
module Essentials
VERSION = "19.1.dev"
VERSION = "21.1"
ERROR_TEXT = ""
MKXPZ_VERSION = "2.4.2/c9378cf"
end

View File

@@ -1,3 +1,6 @@
#===============================================================================
#
#===============================================================================
module PBDebug
@@log = []
@@ -7,28 +10,65 @@ module PBDebug
rescue
PBDebug.log("")
PBDebug.log("**Exception: #{$!.message}")
PBDebug.log("#{$!.backtrace.inspect}")
backtrace = ""
$!.backtrace.each { |line| backtrace += line + "\r\n" }
PBDebug.log(backtrace)
PBDebug.log("")
# if $INTERNAL
pbPrintException($!)
# end
pbPrintException($!) # if $INTERNAL
PBDebug.flush
end
end
def self.flush
if $DEBUG && $INTERNAL && @@log.length>0
File.open("Data/debuglog.txt", "a+b") { |f| f.write("#{@@log}") }
if $DEBUG && $INTERNAL && @@log.length > 0
File.open("Data/debuglog.txt", "a+b") { |f| f.write(@@log.join) }
end
@@log.clear
end
def self.log(msg)
if $DEBUG && $INTERNAL
@@log.push("#{msg}\r\n")
# if @@log.length>1024
PBDebug.flush
# end
echoln msg.gsub("%", "%%")
@@log.push(msg + "\r\n")
PBDebug.flush # if @@log.length > 1024
end
end
def self.log_header(msg)
if $DEBUG && $INTERNAL
echoln Console.markup_style(msg.gsub("%", "%%"), text: :light_purple)
@@log.push(msg + "\r\n")
PBDebug.flush # if @@log.length > 1024
end
end
def self.log_message(msg)
if $DEBUG && $INTERNAL
msg = "\"" + msg + "\""
echoln Console.markup_style(msg.gsub("%", "%%"), text: :dark_gray)
@@log.push(msg + "\r\n")
PBDebug.flush # if @@log.length > 1024
end
end
def self.log_ai(msg)
if $DEBUG && $INTERNAL
msg = "[AI] " + msg
echoln msg.gsub("%", "%%")
@@log.push(msg + "\r\n")
PBDebug.flush # if @@log.length > 1024
end
end
def self.log_score_change(amt, msg)
return if amt == 0
if $DEBUG && $INTERNAL
sign = (amt > 0) ? "+" : "-"
amt_text = sprintf("%3d", amt.abs)
msg = " #{sign}#{amt_text}: #{msg}"
echoln msg.gsub("%", "%%")
@@log.push(msg + "\r\n")
PBDebug.flush # if @@log.length > 1024
end
end

View File

@@ -1,25 +1,24 @@
# To use the console, use the executable explicitly built
# with the console enabled on Windows. On Linux and macOS,
# just launch the executable directly from a terminal.
#===============================================================================
# To use the console, use the executable explicitly built with the console
# enabled on Windows. On Linux and macOS, just launch the executable directly
# from a terminal.
#===============================================================================
module Console
def self.setup_console
return unless $DEBUG
echoln "--------------------------------"
echoln "GPU Cache Max: #{Bitmap.max_size}"
echoln "-------------------------------------------------------------------------------"
echoln "#{System.game_title} Output Window"
echoln "--------------------------------"
echoln "If you are seeing this window, you are running"
echoln "#{System.game_title} in Debug Mode. This means"
echoln "that you're either playing a Debug Version, or"
echoln "you are playing from within RPG Maker XP."
echoln "-------------------------------------------------------------------------------"
echoln "If you can see this window, you are running the game in Debug Mode. This means"
echoln "that you're either playing a debug version of the game, or you're playing from"
echoln "within RPG Maker XP."
echoln ""
echoln "Closing this window will close the game. If"
echoln "you want to get rid of this window, run the"
echoln "program from the Shell, or download a Release"
echoln "version."
echoln ""
echoln "--------------------------------"
echoln "Closing this window will close the game. If you want to get rid of this window,"
echoln "run the program from the Shell, or download a release version of the game."
echoln "-------------------------------------------------------------------------------"
echoln "Debug Output:"
echoln "--------------------------------"
echoln "-------------------------------------------------------------------------------"
echoln ""
end
@@ -36,6 +35,9 @@ module Console
end
end
#===============================================================================
#
#===============================================================================
module Kernel
def echo(string)
return unless $DEBUG
@@ -43,9 +45,199 @@ module Kernel
end
def echoln(string)
caller_info = caller(1..1).first
file, line, method = caller_info.split(":")
echo "#{file}, #{line}:\t"
echo(string)
echo("\r\n")
end
end
Console.setup_console
#===============================================================================
# Console message formatting
#===============================================================================
module Console
module_function
#-----------------------------------------------------------------------------
# echo string into console (example shorthand for common options)
#-----------------------------------------------------------------------------
# heading 1
def echo_h1(msg)
echoln markup_style("*** #{msg} ***", text: :brown)
echoln ""
end
# heading 2
def echo_h2(msg, **options)
echoln markup_style(msg, **options)
echoln ""
end
# heading 3
def echo_h3(msg)
echoln markup(msg)
echoln ""
end
# list item
def echo_li(msg, pad = 0, color = :brown)
echo markup_style(" -> ", text: color)
pad = (pad - msg.length) > 0 ? "." * (pad - msg.length) : ""
echo markup(msg + pad)
end
# list item with line break after
def echoln_li(msg, pad = 0, color = :brown)
self.echo_li(msg, pad, color)
echoln ""
end
# Same as echoln_li but text is in green
def echoln_li_done(msg)
self.echo_li(markup_style(msg, text: :green), 0, :green)
echoln ""
echoln ""
end
# paragraph with markup
def echo_p(msg)
echoln markup(msg)
end
# warning message
def echo_warn(msg)
echoln markup_style("WARNING: #{msg}", text: :yellow)
end
# error message
def echo_error(msg)
echoln markup_style("ERROR: #{msg}", text: :light_red)
end
# status output
def echo_status(status)
if status
echoln markup_style("OK", text: :green)
else
echoln markup_style("FAIL", text: :red)
end
end
# completion output
def echo_done(status)
if status
echoln markup_style("done", text: :green)
else
echoln markup_style("error", text: :red)
end
end
#-----------------------------------------------------------------------------
# Markup options
#-----------------------------------------------------------------------------
def string_colors
{
default: "38", black: "30", red: "31", green: "32", brown: "33",
blue: "34", purple: "35", cyan: "36", gray: "37",
dark_gray: "1;30", light_red: "1;31", light_green: "1;32", yellow: "1;33",
light_blue: "1;34", light_purple: "1;35", light_cyan: "1;36", white: "1;37"
}
end
def background_colors
{
default: "0", black: "40", red: "41", green: "42", brown: "43",
blue: "44", purple: "45", cyan: "46", gray: "47",
dark_gray: "100", light_red: "101", light_green: "102", yellow: "103",
light_blue: "104", light_purple: "105", light_cyan: "106", white: "107"
}
end
def font_options
{
bold: "1", dim: "2", italic: "3", underline: "4", reverse: "7",
hidden: "8"
}
end
# Text markup that turns text between them a certain color
def markup_colors
{
"`" => :cyan, '"' => :purple, "==" => :purple, "$" => :green, "~" => :red
}
end
def markup_options
{
"__" => :underline, "*" => :bold, "|" => :italic
}
end
# apply console coloring
def markup_style(string, text: :default, bg: :default, **options)
# get colors
code_text = string_colors[text]
code_bg = background_colors[bg]
# get options
options_pool = options.select { |key, val| font_options.key?(key) && val }
markup_pool = options_pool.keys.map { |opt| font_options[opt] }.join(";").squeeze
# return formatted string
return "\e[#{code_bg};#{markup_pool};#{code_text}m#{string}\e[0m".squeeze(";")
end
#-----------------------------------------------------------------------------
# Perform markup on text
#-----------------------------------------------------------------------------
def markup_all_options
@markup_all_options ||= markup_colors.merge(markup_options)
end
def markup_component(string, component, key, options)
# trim inner markup content
l = key.length
trimmed = component[l...-l]
# merge markup options
options[trimmed] = {} unless options[trimmed]
options[trimmed].deep_merge!({}.tap do |new_opt|
new_opt[:text] = markup_colors[key] if markup_colors.key?(key)
new_opt[markup_options[key]] = true if markup_options.key?(key)
end)
# remove markup from input string
string.gsub!(component, trimmed)
# return output
return string, options
end
def markup_breakdown(string, options = {})
# iterate through all options
markup_all_options.each_key do |key|
# ensure escape
key_char = key.chars.map { |c| "\\#{c}" }.join
# define regex
regex = "#{key_char}.*?#{key_char}"
# go through matches
string.scan(/#{regex}/).each do |component|
return *markup_breakdown(*markup_component(string, component, key, options))
end
end
# return output
return string, options
end
def markup(string)
# get a breakdown of all markup options
string, options = markup_breakdown(string)
# iterate through each option and apply
options.each do |key, opt|
string.gsub!(key, markup_style(key, **opt))
end
# return string
return string
end
end

View File

@@ -4,12 +4,29 @@
class Reset < Exception
end
def pbGetExceptionMessage(e,_script="")
#===============================================================================
#
#===============================================================================
class EventScriptError < Exception
attr_accessor :event_message
def initialize(message)
super(nil)
@event_message = message
end
end
#===============================================================================
#
#===============================================================================
def pbGetExceptionMessage(e, _script = "")
return e.event_message.dup if e.is_a?(EventScriptError) # Message with map/event ID generated elsewhere
emessage = e.message.dup
emessage.force_encoding(Encoding::UTF_8)
if e.is_a?(Hangup)
case e
when Hangup
emessage = "The script is taking too long. The game will restart."
elsif e.is_a?(Errno::ENOENT)
when Errno::ENOENT
filename = emessage.sub("No such file or directory - ", "")
emessage = "File #{filename} not found."
end
@@ -18,30 +35,28 @@ def pbGetExceptionMessage(e,_script="")
end
def pbPrintException(e)
emessage = ""
if $EVENTHANGUPMSG && $EVENTHANGUPMSG!=""
emessage = $EVENTHANGUPMSG # Message with map/event ID generated elsewhere
$EVENTHANGUPMSG = nil
else
emessage = pbGetExceptionMessage(e)
end
emessage = pbGetExceptionMessage(e)
# begin message formatting
message = "[Pokémon Essentials version #{Essentials::VERSION}]\r\n"
message += "#{Essentials::ERROR_TEXT}\r\n" # For third party scripts to add to
message += "Exception: #{e.class}\r\n"
message += "Message: #{emessage}\r\n"
# show last 10/25 lines of backtrace
message += "\r\nBacktrace:\r\n"
btrace = ""
if e.backtrace
maxlength = ($INTERNAL) ? 25 : 10
e.backtrace[0, maxlength].each { |i| btrace += "#{i}\r\n" }
if !e.is_a?(EventScriptError)
message += "Exception: #{e.class}\r\n"
message += "Message: "
end
message += emessage
# show last 10/25 lines of backtrace
if !e.is_a?(EventScriptError)
message += "\r\n\r\nBacktrace:\r\n"
backtrace_text = ""
if e.backtrace
maxlength = ($INTERNAL) ? 25 : 10
e.backtrace[0, maxlength].each { |i| backtrace_text += "#{i}\r\n" }
end
backtrace_text.gsub!(/Section(\d+)/) { $RGSS_SCRIPTS[$1.to_i][1] } rescue nil
message += backtrace_text
end
btrace.gsub!(/Section(\d+)/) { $RGSS_SCRIPTS[$1.to_i][1] } rescue nil
message += btrace
# output to log
errorlog = "errorlog.txt"
errorlog = RTP.getSaveFileName("errorlog.txt") if (Object.const_defined?(:RTP) rescue false)
File.open(errorlog, "ab") do |f|
f.write("\r\n=================\r\n\r\n[#{Time.now}]\r\n")
f.write(message)
@@ -54,8 +69,8 @@ def pbPrintException(e)
# output message
print("#{message}\r\nThis exception was logged in #{errorlogline}.\r\nHold Ctrl when closing this message to copy it to the clipboard.")
# Give a ~500ms coyote time to start holding Control
t = System.delta
until (System.delta - t) >= 500000
t = System.uptime
until System.uptime - t >= 0.5
Input.update
if Input.press?(Input::CTRL)
Input.clipboard = message

View File

@@ -1,7 +1,9 @@
#===============================================================================
# The Kernel module is extended to include the validate method.
#===============================================================================
module Kernel
private
# Used to check whether method arguments are of a given class or respond to a method.
# @param value_pairs [Hash{Object => Class, Array<Class>, Symbol}] value pairs to validate
# @example Validate a class or method

View File

@@ -1,5 +1,7 @@
#===============================================================================
# The Deprecation module is used to warn game & plugin creators of deprecated
# methods.
#===============================================================================
module Deprecation
module_function
@@ -8,19 +10,21 @@ module Deprecation
# @param removal_version [String] version the method is removed in
# @param alternative [String] preferred alternative method
def warn_method(method_name, removal_version = nil, alternative = nil)
text = _INTL('WARN: usage of deprecated method "{1}" or its alias.', method_name)
text = _INTL('Usage of deprecated method "{1}" or its alias.', method_name)
unless removal_version.nil?
text += _INTL("\nThe method is slated to be"\
" removed in Essentials {1}.", removal_version)
text += "\n" + _INTL("The method is slated to be removed in Essentials {1}.", removal_version)
end
unless alternative.nil?
text += _INTL("\nUse \"{1}\" instead.", alternative)
text += "\n" + _INTL("Use \"{1}\" instead.", alternative)
end
echoln text
Console.echo_warn text
end
end
# The Module class is extended to allow easy deprecation of instance and class methods.
#===============================================================================
# The Module class is extended to allow easy deprecation of instance and class
# methods.
#===============================================================================
class Module
private
@@ -41,11 +45,11 @@ class Module
raise ArgumentError, "#{class_name} does not have method #{aliased_method} defined"
end
delimiter = class_method ? '.' : '#'
delimiter = class_method ? "." : "#"
target.define_method(name) do |*args, **kvargs|
alias_name = format('%s%s%s', class_name, delimiter, name)
aliased_method_name = format('%s%s%s', class_name, delimiter, aliased_method)
alias_name = sprintf("%s%s%s", class_name, delimiter, name)
aliased_method_name = sprintf("%s%s%s", class_name, delimiter, aliased_method)
Deprecation.warn_method(alias_name, removal_in, aliased_method_name)
method(aliased_method).call(*args, **kvargs)
end

View File

@@ -1,27 +1,16 @@
# Using mkxp-z v2.2.0 - https://gitlab.com/mkxp-z/mkxp-z/-/releases/v2.2.0
#===============================================================================
# Using mkxp-z v2.4.2/c9378cf - built 2023-07-07
# https://github.com/mkxp-z/mkxp-z/actions/runs/5482601942
#===============================================================================
$VERBOSE = nil
Font.default_shadow = false if Font.respond_to?(:default_shadow)
Graphics.frame_rate = 40
Encoding.default_internal = Encoding::UTF_8
Encoding.default_external = Encoding::UTF_8
def pbSetWindowText(string)
System.set_window_title(string || System.game_title)
end
class Bitmap
alias mkxp_draw_text draw_text unless method_defined?(:mkxp_draw_text)
def draw_text(x, y, width, height, text, align = 0)
height = text_size(text).height
mkxp_draw_text(x, y, width, height, text, align)
end
end
module Graphics
def self.delta_s
return self.delta.to_f / 1_000_000
end
end
def pbSetResizeFactor(factor)
if !$ResizeInitialized
Graphics.resize_screen(Settings::SCREEN_WIDTH, Settings::SCREEN_HEIGHT)
@@ -35,3 +24,33 @@ def pbSetResizeFactor(factor)
Graphics.center
end
end
#===============================================================================
#
#===============================================================================
class Bitmap
attr_accessor :text_offset_y
alias mkxp_draw_text draw_text unless method_defined?(:mkxp_draw_text)
def draw_text(x, y, width, height = nil, text = "", align = 0)
if x.is_a?(Rect)
x.y -= (@text_offset_y || 0)
# rect, string & alignment
mkxp_draw_text(x, y, width)
else
y -= (@text_offset_y || 0)
height = text_size(text).height
mkxp_draw_text(x, y, width, height, text, align)
end
end
end
#===============================================================================
#
#===============================================================================
if System::VERSION != Essentials::MKXPZ_VERSION
printf(sprintf("\e[1;33mWARNING: mkxp-z version %s detected, but this version of Pokémon Essentials was designed for mkxp-z version %s.\e[0m\r\n",
System::VERSION, Essentials::MKXPZ_VERSION))
printf("\e[1;33mWARNING: Pokémon Essentials may not work properly.\e[0m\r\n")
end

View File

@@ -1,28 +1,25 @@
#===============================================================================
# Reads files of certain format from a directory
# Reads files of certain format from a directory
#===============================================================================
class Dir
#-----------------------------------------------------------------------------
# Reads all files in a directory
#-----------------------------------------------------------------------------
# Reads all files in a directory
def self.get(dir, filters = "*", full = true)
files = []
filters = [filters] if !filters.is_a?(Array)
self.chdir(dir) do
for filter in filters
self.glob(filter){ |f| files.push(full ? (dir + "/" + f) : f) }
filters.each do |filter|
self.glob(filter) { |f| files.push(full ? (dir + "/" + f) : f) }
end
end
return files.sort
end
#-----------------------------------------------------------------------------
# Generates entire file/folder tree from a certain directory
#-----------------------------------------------------------------------------
# Generates entire file/folder tree from a certain directory
def self.all(dir, filters = "*", full = true)
# sets variables for starting
files = []
subfolders = []
for file in self.get(dir, filters, full)
self.get(dir, filters, full).each do |file|
# engages in recursion to read the entire file tree
if self.safe?(file) # Is a directory
subfolders += self.all(file, filters, full)
@@ -33,70 +30,60 @@ class Dir
# returns all found files
return files + subfolders
end
#-----------------------------------------------------------------------------
# Checks for existing directory, gets around accents
#-----------------------------------------------------------------------------
# Checks for existing directory
def self.safe?(dir)
return false if !FileTest.directory?(dir)
ret = false
self.chdir(dir) { ret = true } rescue nil
return ret
return FileTest.directory?(dir)
end
#-----------------------------------------------------------------------------
end
#===============================================================================
# extensions for file class
#===============================================================================
class File
#-----------------------------------------------------------------------------
# Checks for existing file, gets around accents
#-----------------------------------------------------------------------------
def self.safe?(file)
ret = false
self.open(file, 'rb') { ret = true } rescue nil
return ret
# Creates all the required directories for filename path
def self.create(path)
path.gsub!("\\", "/") # Windows compatibility
# get path tree
dirs = path.split("/")
full = ""
dirs.each do |dir|
full += dir + "/"
# creates directories
self.mkdir(full) if !self.safe?(full)
end
end
# Generates entire folder tree from a certain directory
def self.all_dirs(dir)
# sets variables for starting
dirs = []
self.get(dir, "*", true).each do |file|
# engages in recursion to read the entire folder tree
dirs += self.all_dirs(file) if self.safe?(file)
end
# returns all found directories
return dirs.length > 0 ? (dirs + [dir]) : [dir]
end
# Deletes all the files in a directory and all the sub directories (allows for non-empty dirs)
def self.delete_all(dir)
# delete all files in dir
self.all(dir).each { |f| File.delete(f) }
# delete all dirs in dir
self.all_dirs(dir).each { |f| Dir.delete(f) }
end
#-----------------------------------------------------------------------------
end
#===============================================================================
# Checking for files and directories
#===============================================================================
# Works around a problem with FileTest.directory if directory contains accent marks
def safeIsDirectory?(f)
ret = false
Dir.chdir(f) { ret = true } rescue nil
return ret
end
# Works around a problem with FileTest.exist if path contains accent marks
def safeExists?(f)
return FileTest.exist?(f) if f[/\A[\x20-\x7E]*\z/]
ret = false
begin
File.open(f,"rb") { ret = true }
rescue Errno::ENOENT, Errno::EINVAL, Errno::EACCES
ret = false
end
return ret
end
# Similar to "Dir.glob", but designed to work around a problem with accessing
# files if a path contains accent marks.
# "dir" is the directory path, "wildcard" is the filename pattern to match.
def safeGlob(dir,wildcard)
def safeGlob(dir, wildcard)
ret = []
afterChdir = false
begin
Dir.chdir(dir) {
Dir.chdir(dir) do
afterChdir = true
Dir.glob(wildcard) { |f| ret.push(dir+"/"+f) }
}
Dir.glob(wildcard) { |f| ret.push(dir + "/" + f) }
end
rescue Errno::ENOENT
raise if afterChdir
end
@@ -108,8 +95,8 @@ end
def pbResolveAudioSE(file)
return nil if !file
if RTP.exists?("Audio/SE/"+file,["",".wav",".mp3",".ogg"])
return RTP.getPath("Audio/SE/"+file,["",".wav",".mp3",".ogg"])
if RTP.exists?("Audio/SE/" + file, ["", ".wav", ".ogg", ".mp3", ".wma"])
return RTP.getPath("Audio/SE/" + file, ["", ".wav", ".ogg", ".mp3", ".wma"])
end
return nil
end
@@ -118,19 +105,19 @@ end
# archives. Returns nil if the path can't be found.
def pbResolveBitmap(x)
return nil if !x
noext = x.gsub(/\.(bmp|png|gif|jpg|jpeg)$/,"")
noext = x.gsub(/\.(bmp|png|gif|jpg|jpeg)$/, "")
filename = nil
# RTP.eachPathFor(x) { |path|
# filename = pbTryString(path) if !filename
# filename = pbTryString(path+".gif") if !filename
# filename = pbTryString(path + ".gif") if !filename
# }
RTP.eachPathFor(noext) { |path|
filename = pbTryString(path+".png") if !filename
filename = pbTryString(path+".gif") if !filename
# filename = pbTryString(path+".jpg") if !filename
# filename = pbTryString(path+".jpeg") if !filename
# filename = pbTryString(path+".bmp") if !filename
}
RTP.eachPathFor(noext) do |path|
filename = pbTryString(path + ".png") if !filename
filename = pbTryString(path + ".gif") if !filename
# filename = pbTryString(path + ".jpg") if !filename
# filename = pbTryString(path + ".jpeg") if !filename
# filename = pbTryString(path + ".bmp") if !filename
end
return filename
end
@@ -157,7 +144,7 @@ def canonicalize(c)
pos = -1
ret = []
retstr = ""
for x in csplit
csplit.each do |x|
if x == ".."
if pos >= 0
ret.delete_at(pos)
@@ -168,50 +155,51 @@ def canonicalize(c)
pos += 1
end
end
for i in 0...ret.length
ret.length.times do |i|
retstr += "/" if i > 0
retstr += ret[i]
end
return retstr
end
#===============================================================================
#
#===============================================================================
module RTP
@rtpPaths = nil
def self.exists?(filename,extensions=[])
def self.exists?(filename, extensions = [])
return false if nil_or_empty?(filename)
eachPathFor(filename) { |path|
return true if safeExists?(path)
for ext in extensions
return true if safeExists?(path+ext)
eachPathFor(filename) do |path|
return true if FileTest.exist?(path)
extensions.each do |ext|
return true if FileTest.exist?(path + ext)
end
}
end
return false
end
def self.getImagePath(filename)
return self.getPath(filename,["",".png",".gif"]) # ".jpg", ".jpeg", ".bmp"
return self.getPath(filename, ["", ".png", ".gif"]) # ".jpg", ".jpeg", ".bmp"
end
def self.getAudioPath(filename)
return self.getPath(filename,["",".mp3",".wav",".wma",".mid",".ogg",".midi"])
return self.getPath(filename, ["", ".wav", ".ogg", ".mp3", ".midi", ".mid", ".wma"])
end
def self.getPath(filename,extensions=[])
def self.getPath(filename, extensions = [])
return filename if nil_or_empty?(filename)
eachPathFor(filename) { |path|
return path if safeExists?(path)
for ext in extensions
file = path+ext
return file if safeExists?(file)
eachPathFor(filename) do |path|
return path if FileTest.exist?(path)
extensions.each do |ext|
file = path + ext
return file if FileTest.exist?(file)
end
}
end
return filename
end
# Gets the absolute RGSS paths for the given file name
# Gets the absolute RGSS paths for the given file name
def self.eachPathFor(filename)
return if !filename
if filename[/^[A-Za-z]\:[\/\\]/] || filename[/^[\/\\]/]
@@ -219,13 +207,13 @@ module RTP
yield filename
else
# relative path
RTP.eachPath { |path|
if path=="./"
RTP.eachPath do |path|
if path == "./"
yield filename
else
yield path+filename
yield path + filename
end
}
end
end
end
@@ -237,11 +225,9 @@ module RTP
def self.eachPath
# XXX: Use "." instead of Dir.pwd because of problems retrieving files if
# the current directory contains an accent mark
yield ".".gsub(/[\/\\]/,"/").gsub(/[\/\\]$/,"")+"/"
yield ".".gsub(/[\/\\]/, "/").gsub(/[\/\\]$/, "") + "/"
end
private
def self.getSaveFileName(fileName)
File.join(getSaveFolder, fileName)
end
@@ -257,50 +243,47 @@ module RTP
end
end
#===============================================================================
#
#===============================================================================
module FileTest
Image_ext = ['.png', '.gif'] # '.jpg', '.jpeg', '.bmp',
Audio_ext = ['.mp3', '.mid', '.midi', '.ogg', '.wav', '.wma']
IMAGE_EXTENSIONS = [".png", ".gif"] # ".jpg", ".jpeg", ".bmp",
AUDIO_EXTENSIONS = [".wav", ".ogg", ".mp3", ".midi", ".mid", ".wma"]
def self.audio_exist?(filename)
return RTP.exists?(filename,Audio_ext)
return RTP.exists?(filename, AUDIO_EXTENSIONS)
end
def self.image_exist?(filename)
return RTP.exists?(filename,Image_ext)
return RTP.exists?(filename, IMAGE_EXTENSIONS)
end
end
#===============================================================================
#
#===============================================================================
# Used to determine whether a data file exists (rather than a graphics or
# audio file). Doesn't check RTP, but does check encrypted archives.
# Note: pbGetFileChar checks anything added in MKXP's RTP setting,
# and matching mount points added through System.mount
# NOTE: pbGetFileChar checks anything added in MKXP's RTP setting, and matching
# mount points added through System.mount.
def pbRgssExists?(filename)
if safeExists?("./Game.rgssad")
return pbGetFileChar(filename)!=nil
else
filename = canonicalize(filename)
return safeExists?(filename)
end
return !pbGetFileChar(filename).nil? if FileTest.exist?("./Game.rgssad")
filename = canonicalize(filename)
return FileTest.exist?(filename)
end
# Opens an IO, even if the file is in an encrypted archive.
# Doesn't check RTP for the file.
# Note: load_data checks anything added in MKXP's RTP setting,
# and matching mount points added through System.mount
def pbRgssOpen(file,mode=nil)
#File.open("debug.txt","ab") { |fw| fw.write([file,mode,Time.now.to_f].inspect+"\r\n") }
if !safeExists?("./Game.rgssad")
# NOTE: load_data checks anything added in MKXP's RTP setting, and matching
# mount points added through System.mount.
def pbRgssOpen(file, mode = nil)
# File.open("debug.txt", "ab") { |fw| fw.write([file, mode, Time.now.to_f].inspect + "\r\n") }
if !FileTest.exist?("./Game.rgssad")
if block_given?
File.open(file,mode) { |f| yield f }
File.open(file, mode) { |f| yield f }
return nil
else
return File.open(file,mode)
return File.open(file, mode)
end
end
file = canonicalize(file)
@@ -318,9 +301,9 @@ end
# encrypted archives.
def pbGetFileChar(file)
canon_file = canonicalize(file)
if !safeExists?("./Game.rgssad")
return nil if !safeExists?(canon_file)
return nil if file.last == '/' # Is a directory
if !FileTest.exist?("./Game.rgssad")
return nil if !FileTest.exist?(canon_file)
return nil if file.last == "/" # Is a directory
begin
File.open(canon_file, "rb") { |f| return f.read(1) } # read one byte
rescue Errno::ENOENT, Errno::EINVAL, Errno::EACCES, Errno::EISDIR
@@ -338,20 +321,19 @@ end
def pbTryString(x)
ret = pbGetFileChar(x)
return (ret!=nil && ret!="") ? x : nil
return nil_or_empty?(ret) ? nil : x
end
# Gets the contents of a file. Doesn't check RTP, but does check
# encrypted archives.
# Note: load_data will check anything added in MKXP's RTP setting,
# and matching mount points added through System.mount
# NOTE: load_data will check anything added in MKXP's RTP setting, and matching
# mount points added through System.mount.
def pbGetFileString(file)
file = canonicalize(file)
if !safeExists?("./Game.rgssad")
return nil if !safeExists?(file)
if !FileTest.exist?("./Game.rgssad")
return nil if !FileTest.exist?(file)
begin
File.open(file,"rb") { |f| return f.read } # read all data
File.open(file, "rb") { |f| return f.read } # read all data
rescue Errno::ENOENT, Errno::EINVAL, Errno::EACCES
return nil
end
@@ -365,22 +347,22 @@ def pbGetFileString(file)
return str
end
#===============================================================================
#
#===============================================================================
class StringInput
include Enumerable
attr_reader :lineno, :string
class << self
def new( str )
def new(str)
if block_given?
begin
f = super
yield f
ensure
f.close if f
f&.close
end
else
super
@@ -389,21 +371,19 @@ class StringInput
alias open new
end
def initialize( str )
def initialize(str)
@string = str
@pos = 0
@closed = false
@lineno = 0
end
attr_reader :lineno,:string
def inspect
return "#<#{self.class}:#{@closed ? 'closed' : 'open'},src=#{@string[0,30].inspect}>"
return "#<#{self.class}:#{@closed ? 'closed' : 'open'},src=#{@string[0, 30].inspect}>"
end
def close
raise IOError, 'closed stream' if @closed
raise IOError, "closed stream" if @closed
@pos = nil
@closed = true
end
@@ -411,7 +391,7 @@ class StringInput
def closed?; @closed; end
def pos
raise IOError, 'closed stream' if @closed
raise IOError, "closed stream" if @closed
[@pos, @string.size].min
end
@@ -421,8 +401,8 @@ class StringInput
def pos=(value); seek(value); end
def seek(offset, whence=IO::SEEK_SET)
raise IOError, 'closed stream' if @closed
def seek(offset, whence = IO::SEEK_SET)
raise IOError, "closed stream" if @closed
case whence
when IO::SEEK_SET then @pos = offset
when IO::SEEK_CUR then @pos += offset
@@ -436,12 +416,12 @@ class StringInput
end
def eof?
raise IOError, 'closed stream' if @closed
raise IOError, "closed stream" if @closed
@pos > @string.size
end
def each( &block )
raise IOError, 'closed stream' if @closed
def each(&block)
raise IOError, "closed stream" if @closed
begin
@string.each(&block)
ensure
@@ -450,14 +430,15 @@ class StringInput
end
def gets
raise IOError, 'closed stream' if @closed
if idx = @string.index(?\n, @pos)
raise IOError, "closed stream" if @closed
idx = @string.index("\n", @pos)
if idx
idx += 1 # "\n".size
line = @string[ @pos ... idx ]
line = @string[@pos...idx]
@pos = idx
@pos += 1 if @pos == @string.size
else
line = @string[ @pos .. -1 ]
line = @string[@pos..-1]
@pos = @string.size + 1
end
@lineno += 1
@@ -465,18 +446,18 @@ class StringInput
end
def getc
raise IOError, 'closed stream' if @closed
raise IOError, "closed stream" if @closed
ch = @string[@pos]
@pos += 1
@pos += 1 if @pos == @string.size
ch
end
def read( len = nil )
raise IOError, 'closed stream' if @closed
def read(len = nil)
raise IOError, "closed stream" if @closed
if !len
return nil if eof?
rest = @string[@pos ... @string.size]
rest = @string[@pos...@string.size]
@pos = @string.size + 1
return rest
end
@@ -485,8 +466,6 @@ class StringInput
@pos += 1 if @pos == @string.size
str
end
def read_all; read(); end
alias read_all read
alias sysread read
end

View File

@@ -1,3 +1,6 @@
#===============================================================================
#
#===============================================================================
module FileInputMixin
def fgetb
ret = 0
@@ -67,7 +70,7 @@ module FileInputMixin
self.pos = 0
offset = fgetdw >> 3
return 0 if index >= offset
self.pos = index * 8 + 4
self.pos = (index * 8) + 4
return fgetdw
end
@@ -85,6 +88,9 @@ module FileInputMixin
end
end
#===============================================================================
#
#===============================================================================
module FileOutputMixin
def fputb(b)
b &= 0xFF
@@ -108,27 +114,32 @@ module FileOutputMixin
end
end
#===============================================================================
#
#===============================================================================
class File < IO
=begin
unless defined?(debugopen)
class << self
alias debugopen open
end
end
# unless defined?(debugopen)
# class << self
# alias debugopen open
# end
# end
# def open(f, m = "r")
# debugopen("debug.txt", "ab") { |file| file.write([f, m, Time.now.to_f].inspect + "\r\n") }
# if block_given?
# debugopen(f, m) { |file| yield file }
# else
# return debugopen(f, m)
# end
# end
def open(f, m = "r")
debugopen("debug.txt", "ab") { |file| file.write([f, m, Time.now.to_f].inspect + "\r\n") }
if block_given?
debugopen(f, m) { |file| yield file }
else
return debugopen(f, m)
end
end
=end
include FileInputMixin
include FileOutputMixin
end
#===============================================================================
#
#===============================================================================
class StringInput
include FileInputMixin
@@ -137,7 +148,7 @@ class StringInput
end
def each_byte
while !eof?
until eof?
yield getc
end
end
@@ -145,6 +156,9 @@ class StringInput
def binmode; end
end
#===============================================================================
#
#===============================================================================
class StringOutput
include FileOutputMixin
end

View File

@@ -1,37 +1,35 @@
#############################
#
#===============================================================================
# HTTP utility functions
#
#############################
def pbPostData(url, postdata, filename=nil, depth=0)
#===============================================================================
def pbPostData(url, postdata, filename = nil, depth = 0)
if url[/^http:\/\/([^\/]+)(.*)$/]
host = $1
path = $2
path = "/" if path.length==0
# path = $2
# path = "/" if path.length == 0
userAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.14) Gecko/2009082707 Firefox/3.0.14"
body = postdata.map { |key, value|
body = postdata.map do |key, value|
keyString = key.to_s
valueString = value.to_s
keyString.gsub!(/[^a-zA-Z0-9_\.\-]/n) { |s| sprintf('%%%02x', s[0]) }
valueString.gsub!(/[^a-zA-Z0-9_\.\-]/n) { |s| sprintf('%%%02x', s[0]) }
keyString.gsub!(/[^a-zA-Z0-9_\.\-]/n) { |s| sprintf("%%%02x", s[0]) }
valueString.gsub!(/[^a-zA-Z0-9_\.\-]/n) { |s| sprintf("%%%02x", s[0]) }
next "#{keyString}=#{valueString}"
}.join('&')
end.join("&")
ret = HTTPLite.post_body(
url,
body,
"application/x-www-form-urlencoded",
{
"Host" => host, # might not be necessary
"Host" => host, # might not be necessary
"Proxy-Connection" => "Close",
"Content-Length" => body.bytesize.to_s,
"Pragma" => "no-cache",
"User-Agent" => userAgent
"Content-Length" => body.bytesize.to_s,
"Pragma" => "no-cache",
"User-Agent" => userAgent
}
) rescue ""
return ret if !ret.is_a?(Hash)
return "" if ret[:status] != 200
return ret[:body] if !filename
File.open(filename, "wb"){|f|f.write(ret[:body])}
File.open(filename, "wb") { |f| f.write(ret[:body]) }
return ""
end
return ""
@@ -40,8 +38,8 @@ end
def pbDownloadData(url, filename = nil, authorization = nil, depth = 0, &block)
headers = {
"Proxy-Connection" => "Close",
"Pragma" => "no-cache",
"User-Agent" => "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.14) Gecko/2009082707 Firefox/3.0.14"
"Pragma" => "no-cache",
"User-Agent" => "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.14) Gecko/2009082707 Firefox/3.0.14"
}
headers["authorization"] = authorization if authorization
ret = HTTPLite.get(url, headers) rescue ""
@@ -63,7 +61,7 @@ end
def pbDownloadToFile(url, file)
begin
pbDownloadData(url,file)
pbDownloadData(url, file)
rescue
end
end
@@ -79,7 +77,7 @@ end
def pbPostToFile(url, postdata, file)
begin
pbPostData(url, postdata,file)
pbPostData(url, postdata, file)
rescue
end
end

View File

@@ -2,7 +2,7 @@
# class Object
#===============================================================================
class Object
alias full_inspect inspect
alias full_inspect inspect unless method_defined?(:full_inspect)
def inspect
return "#<#{self.class}>"
@@ -23,32 +23,21 @@ end
#===============================================================================
class String
def starts_with_vowel?
return ['a', 'e', 'i', 'o', 'u'].include?(self[0, 1].downcase)
return ["a", "e", "i", "o", "u"].include?(self[0].downcase)
end
def first(n = 1)
return self[0...n]
end
def first(n = 1); return self[0...n]; end
def last(n = 1)
return self[-n..-1] || self
end
def last(n = 1); return self[-n..-1] || self; end
def blank?
blank = true
s = self.scan(/./)
for l in s
blank = false if l != ""
end
return blank
end
def blank?; return self.strip.empty?; end
def cut(bitmap, width)
string = self
width -= bitmap.text_size("...").width
string_width = 0
text = []
for char in string.scan(/./)
string.scan(/./).each do |char|
wdh = bitmap.text_size(char).width
next if (wdh + string_width) > width
string_width += wdh
@@ -56,19 +45,30 @@ class String
end
text.push("...") if text.length < string.length
new_string = ""
for char in text
text.each do |char|
new_string += char
end
return new_string
end
def numeric?
return !self[/\A[+-]?\d+(?:\.\d+)?\Z/].nil?
end
end
#===============================================================================
# class Numeric
#===============================================================================
class Numeric
# Turns a number into a string formatted like 12,345,678.
# Turns a number into a string formatted like 12,345,678. Some languages use
# different characters as the thousands separator.
def to_s_formatted
case System.user_language[0..1]
when "fr", "es"
return self.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\1 ').reverse
when "it", "de"
return self.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\1.').reverse
end
return self.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\1,').reverse
end
@@ -79,17 +79,47 @@ class Numeric
_INTL("twelve"), _INTL("thirteen"), _INTL("fourteen"), _INTL("fifteen"),
_INTL("sixteen"), _INTL("seventeen"), _INTL("eighteen"), _INTL("nineteen"),
_INTL("twenty")]
return ret[self] if self.is_a?(Integer) && self >= 0 && self <= ret.length
return ret[self] if self.is_a?(Integer) && self >= 0 && self <= ret.length - 1
return self.to_s
end
def to_ordinal
ret = [_INTL("zeroth"), _INTL("first"), _INTL("second"), _INTL("third"),
_INTL("fourth"), _INTL("fifth"), _INTL("sixth"), _INTL("seventh"),
_INTL("eighth"), _INTL("ninth"), _INTL("tenth"), _INTL("eleventh"),
_INTL("twelfth"), _INTL("thirteenth"), _INTL("fourteenth"), _INTL("fifteenth"),
_INTL("sixteenth"), _INTL("seventeenth"), _INTL("eighteenth"), _INTL("nineteenth"),
_INTL("twentieth")]
return ret[self] if self.is_a?(Integer) && self >= 0 && self <= ret.length - 1
return self.to_ord
end
# Returns "1st", "2nd", "3rd", etc.
def to_ord
return self.to_s if !self.is_a?(Integer)
ret = self.to_s
if ((self % 100) / 10) == 1 # 10-19
ret += "th"
elsif (self % 10) == 1
ret += "st"
elsif (self % 10) == 2
ret += "nd"
elsif (self % 10) == 3
ret += "rd"
else
ret += "th"
end
return ret
end
end
#===============================================================================
# class Array
#===============================================================================
class Array
def ^(other) # xor of two arrays
return (self|other) - (self&other)
# xor of two arrays
def ^(other)
return (self | other) - (self & other)
end
def swap(val1, val2)
@@ -100,6 +130,29 @@ class Array
end
end
#===============================================================================
# class Hash
#===============================================================================
class Hash
def deep_merge(hash)
merged_hash = self.clone
merged_hash.deep_merge!(hash) if hash.is_a?(Hash)
return merged_hash
end
def deep_merge!(hash)
# failsafe
return unless hash.is_a?(Hash)
hash.each do |key, val|
if self[key].is_a?(Hash)
self[key].deep_merge!(val)
else
self[key] = val
end
end
end
end
#===============================================================================
# module Enumerable
#===============================================================================
@@ -111,6 +164,238 @@ module Enumerable
end
end
#===============================================================================
# Collision testing
#===============================================================================
class Rect < Object
def contains?(cx, cy)
return cx >= self.x && cx < self.x + self.width &&
cy >= self.y && cy < self.y + self.height
end
end
#===============================================================================
# class File
#===============================================================================
class File
# Copies the source file to the destination path.
def self.copy(source, destination)
data = ""
t = System.uptime
File.open(source, "rb") do |f|
loop do
r = f.read(4096)
break if !r
if System.uptime - t >= 5
t += 5
Graphics.update
end
data += r
end
end
File.delete(destination) if File.file?(destination)
f = File.new(destination, "wb")
f.write data
f.close
end
# Copies the source to the destination and deletes the source.
def self.move(source, destination)
File.copy(source, destination)
File.delete(source)
end
end
#===============================================================================
# class Color
#===============================================================================
class Color
# alias for old constructor
alias init_original initialize unless self.private_method_defined?(:init_original)
# New constructor, accepts RGB values as well as a hex number or string value.
def initialize(*args)
pbPrintException("Wrong number of arguments! At least 1 is needed!") if args.length < 1
case args.length
when 1
case args.first
when Integer
hex = args.first.to_s(16)
when String
try_rgb_format = args.first.split(",")
init_original(*try_rgb_format.map(&:to_i)) if try_rgb_format.length.between?(3, 4)
hex = args.first.delete("#")
end
pbPrintException("Wrong type of argument given!") if !hex
r = hex[0...2].to_i(16)
g = hex[2...4].to_i(16)
b = hex[4...6].to_i(16)
when 3
r, g, b = *args
end
init_original(r, g, b) if r && g && b
init_original(*args)
end
def self.new_from_rgb(param)
return Font.default_color if !param
base_int = param.to_i(16)
case param.length
when 8 # 32-bit hex
return Color.new(
(base_int >> 24) & 0xFF,
(base_int >> 16) & 0xFF,
(base_int >> 8) & 0xFF,
(base_int) & 0xFF
)
when 6 # 24-bit hex
return Color.new(
(base_int >> 16) & 0xFF,
(base_int >> 8) & 0xFF,
(base_int) & 0xFF
)
when 4 # 15-bit hex
return Color.new(
((base_int) & 0x1F) << 3,
((base_int >> 5) & 0x1F) << 3,
((base_int >> 10) & 0x1F) << 3
)
when 1, 2 # Color number
case base_int
when 0 then return Color.white
when 1 then return Color.blue
when 2 then return Color.red
when 3 then return Color.green
when 4 then return Color.cyan
when 5 then return Color.pink
when 6 then return Color.yellow
when 7 then return Color.gray
else return Font.default_color
end
end
return Font.default_color
end
# @return [String] the 15-bit representation of this color in a string, ignoring its alpha
def to_rgb15
ret = (self.red.to_i >> 3)
ret |= ((self.green.to_i >> 3) << 5)
ret |= ((self.blue.to_i >> 3) << 10)
return sprintf("%04X", ret)
end
# @return [String] this color in the format "RRGGBB", ignoring its alpha
def to_rgb24
return sprintf("%02X%02X%02X", self.red.to_i, self.green.to_i, self.blue.to_i)
end
# @return [String] this color in the format "RRGGBBAA" (or "RRGGBB" if this color's alpha is 255)
def to_rgb32(always_include_alpha = false)
if self.alpha.to_i == 255 && !always_include_alpha
return sprintf("%02X%02X%02X", self.red.to_i, self.green.to_i, self.blue.to_i)
end
return sprintf("%02X%02X%02X%02X", self.red.to_i, self.green.to_i, self.blue.to_i, self.alpha.to_i)
end
# @return [String] this color in the format "#RRGGBB", ignoring its alpha
def to_hex
return "#" + to_rgb24
end
# @return [Integer] this color in RGB format converted to an integer
def to_i
return self.to_rgb24.to_i(16)
end
# @return [Color] the contrasting color to this one
def get_contrast_color
r = self.red
g = self.green
b = self.blue
yuv = [
(r * 0.299) + (g * 0.587) + (b * 0.114),
(r * -0.1687) + (g * -0.3313) + (b * 0.500) + 0.5,
(r * 0.500) + (g * -0.4187) + (b * -0.0813) + 0.5
]
if yuv[0] < 127.5
yuv[0] += (255 - yuv[0]) / 2
else
yuv[0] = yuv[0] / 2
end
return Color.new(
yuv[0] + (1.4075 * (yuv[2] - 0.5)),
yuv[0] - (0.3455 * (yuv[1] - 0.5)) - (0.7169 * (yuv[2] - 0.5)),
yuv[0] + (1.7790 * (yuv[1] - 0.5)),
self.alpha
)
end
# Converts the provided hex string/24-bit integer to RGB values.
def self.hex_to_rgb(hex)
hex = hex.delete("#") if hex.is_a?(String)
hex = hex.to_s(16) if hex.is_a?(Numeric)
r = hex[0...2].to_i(16)
g = hex[2...4].to_i(16)
b = hex[4...6].to_i(16)
return r, g, b
end
# Parses the input as a Color and returns a Color object made from it.
def self.parse(color)
case color
when Color
return color
when String, Numeric
return Color.new(color)
end
# returns nothing if wrong input
return nil
end
# Returns color object for some commonly used colors.
def self.red; return Color.new(255, 128, 128); end
def self.green; return Color.new(128, 255, 128); end
def self.blue; return Color.new(128, 128, 255); end
def self.yellow; return Color.new(255, 255, 128); end
def self.magenta; return Color.new(255, 0, 255); end
def self.cyan; return Color.new(128, 255, 255); end
def self.white; return Color.new(255, 255, 255); end
def self.gray; return Color.new(192, 192, 192); end
def self.black; return Color.new( 0, 0, 0); end
def self.pink; return Color.new(255, 128, 255); end
def self.orange; return Color.new(255, 155, 0); end
def self.purple; return Color.new(155, 0, 255); end
def self.brown; return Color.new(112, 72, 32); end
end
#===============================================================================
# Wrap code blocks in a class which passes data accessible as instance variables
# within the code block.
#
# wrapper = CallbackWrapper.new { puts @test }
# wrapper.set(test: "Hi")
# wrapper.execute #=> "Hi"
#===============================================================================
class CallbackWrapper
@params = {}
def initialize(&block)
@code_block = block
end
def execute(given_block = nil, *args)
execute_block = given_block || @code_block
@params.each do |key, value|
args.instance_variable_set("@#{key}", value)
end
args.instance_eval(&execute_block)
end
def set(params = {})
@params = params
end
end
#===============================================================================
# Kernel methods
#===============================================================================
@@ -132,11 +417,26 @@ class << Kernel
return oldRand(a)
end
elsif a.nil?
return (b) ? oldRand(b) : oldRand(2)
return oldRand(b)
end
return oldRand
end
end
def nil_or_empty?(string)
return string.nil? || !string.is_a?(String) || string.size == 0
end
#===============================================================================
# Linear interpolation between two values, given the duration of the change and
# either:
# - the time passed since the start of the change (delta), or
# - the start time of the change (delta) and the current time (now)
#===============================================================================
def lerp(start_val, end_val, duration, delta, now = nil)
return end_val if duration <= 0
delta = now - delta if now
return start_val if delta <= 0
return end_val if delta >= duration
return start_val + ((end_val - start_val) * delta / duration.to_f)
end

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,6 @@
#===============================================================================
#
#===============================================================================
module Input
USE = C
BACK = B
@@ -16,18 +19,17 @@ module Input
def self.update
update_KGC_ScreenCapture
if trigger?(Input::F8)
pbScreenCapture
end
pbScreenCapture if trigger?(Input::F8)
end
end
#===============================================================================
#
#===============================================================================
module Mouse
module_function
# Returns the position of the mouse relative to the game window.
def getMousePos(catch_anywhere = false)
return nil unless System.mouse_in_window || catch_anywhere
def self.getMousePos(catch_anywhere = false)
return nil unless Input.mouse_in_window || catch_anywhere
return Input.mouse_x, Input.mouse_y
end
end

View File

@@ -1,8 +1,8 @@
#==============================================================================#
# Plugin Manager #
# by Marin #
# support for external plugin scripts by Luka S.J. #
# tweaked by Maruno #
# Support for external plugin scripts by Luka S.J. #
# Tweaked by Maruno #
#------------------------------------------------------------------------------#
# Provides a simple interface that allows plugins to require dependencies #
# at specific versions, and to specify incompatibilities between plugins. #
@@ -12,181 +12,118 @@
#------------------------------------------------------------------------------#
# Usage: #
# #
# A Pokémon Essentials plugin should register itself using the PluginManager. #
# The simplest way to do so, for a plugin without dependencies, is as follows: #
# Each plugin should have its own folder in the "Plugins" folder found in the #
# main directory. The "Plugins" folder is similar in concept to the "PBS" #
# folder, in that its contents are compiled and recorded as existing. The #
# plugin's script file(s) are placed in its folder - they must be .rb files. #
# #
# PluginManager.register({ #
# :name => "Basic Plugin", #
# :version => "1.0", #
# :link => "https://reliccastle.com/link-to-the-plugin/", #
# :credits => "Marin" #
# }) #
# A plugin's folder must also contain a "meta.txt" file. This file is what #
# makes Essentials recognise that the plugin exists, and contains important #
# information about the plugin; if this file does not exist, the folder's #
# contents are ignored. Each line in this file is a property. #
# #
# The link portion here is optional, but recommended. This will be shown in #
# the error message if the PluginManager detects that this plugin needs to be #
# updated. #
# Required lines: #
# #
# A plugin's version should be in the format X.Y.Z, but the number of digits #
# you use does not matter. You can also use Xa, Xb, Xc, Ya, etc. #
# What matters is that you use it consistently, so that it can be compared. #
# Name = Simple Extension The plugin's name #
# Version = 1.0 The plugin's version #
# Essentials = 19.1,20 Compatible version(s) of Essentials #
# Link = https://reliccastle.com/link-to-the-plugin/ #
# Credits = Luka S.J.,Maruno,Marin One or more names #
# #
# IF there are multiple people to credit, their names should be in an array. #
# If there is only one credit, it does not need an array: #
# A plugin's version should be in the format X or X.Y or X.Y.Z, where X/Y/Z #
# are numbers. You can also use Xa, Xb, Xc, Ya, etc. What matters is that you #
# use version numbers consistently for your plugin. A later version will be #
# alphanumerically higher than an older version. #
# #
# :credits => "Marin" #
# :credits => ["Marin", "Maruno"], #
# Plugins can interact with each other in several ways, such as requiring #
# another one to exist or by clashing with each other. These interactions are #
# known as dependencies and conflicts. The lines below are all optional, and #
# go in "meta.txt" to define how your plugin works (or doesn't work) with #
# others. You can have multiples of each of these lines. #
# #
# Requires = Basic Plugin Must have this plugin (any version) #
# Requires = Useful Utils,1.1 Must have this plugin/min. version #
# Exact = Scene Tweaks,2 Must have this plugin/version #
# Optional = Extended Windows,1.2 If this plugin exists, load it first #
# Conflicts = Complex Extension Incompatible plugin #
# #
# A plugin that depends on another one ("Requires"/"Exact"/"Optional") will #
# make that other plugin be loaded first. The "Optional" line is for a plugin #
# which isn't necessary, but if it does exist in the same project, it must be #
# at the given version or higher. #
# #
# Dependency: #
# When plugins are compiled, their scripts are stored in the file #
# "PluginScripts.rxdata" in the "Data" folder. Dependencies defined above will #
# ensure that they are loaded in a suitable order. Scripts within a plugin are #
# loaded alphanumerically, going through subfolders depth-first. #
# #
# A plugin can require another plugin to be installed in order to work. For #
# example, the "Simple Extension" plugin depends on the above "Basic Plugin" #
# like so: #
# #
# PluginManager.register({ #
# :name => "Simple Extension", #
# :version => "1.0", #
# :link => "https://reliccastle.com/link-to-the-plugin/", #
# :credits => ["Marin", "Maruno"], #
# :dependencies => ["Basic Plugin"] #
# }) #
# #
# If there are multiple dependencies, they should be listed in an array. If #
# there is only one dependency, it does not need an array: #
# #
# :dependencies => "Basic Plugin" #
# #
# To require a minimum version of a dependency plugin, you should turn the #
# dependency's name into an array which contains the name and the version #
# (both as strings). For example, to require "Basic Plugin" version 1.2 or #
# higher, you would write: #
# #
# :dependencies => [ #
# ["Basic Plugin", "1.2"] #
# ] #
# #
# To require a specific version (no higher and no lower) of a dependency #
# plugin, you should add the :exact flag as the first thing in the array for #
# that dependency: #
# #
# :dependencies => [ #
# [:exact, "Basic Plugin", "1.2"] #
# ] #
# #
# If your plugin can work without another plugin, but it is incompatible with #
# an old version of that other plugin, you should list it as an optional #
# dependency. If that other plugin is present in a game, then this optional #
# dependency will check whether it meets the minimum version required for your #
# plugin. Write it in the same way as any other dependency as described above, #
# but use the :optional flag instead. #
# #
# :dependencies => [ #
# [:optional, "QoL Improvements", "1.1"] #
# ] #
# #
# The :optional_exact flag is a combination of :optional and :exact. #
# #
# #
# #
# Incompatibility: #
# #
# If your plugin is known to be incompatible with another plugin, you should #
# list that other plugin as such. Only one of the two plugins needs to list #
# that it is incompatible with the other. #
# #
# PluginManager.register({ #
# :name => "QoL Improvements", #
# :version => "1.0", #
# :link => "https://reliccastle.com/link-to-the-plugin/", #
# :credits => "Marin", #
# :incompatibilities => [ #
# "Simple Extension" #
# ] #
# }) #
# The "Plugins" folder should be deleted when the game is released. Scripts in #
# there are compiled, but any other files used by a plugin (graphics/audio) #
# should go into other folders and not the plugin's folder. #
# #
#------------------------------------------------------------------------------#
# Plugin folder: #
# The code behind plugins: #
# #
# The Plugin folder is treated like the PBS folder, but for script files for #
# plugins. Each plugin has its own folder within the Plugin folder. Each #
# plugin must have a meta.txt file in its folder, which contains information #
# about that plugin. Folders without this meta.txt file are ignored. #
# When a plugin's "meta.txt" file is read, its contents are registered in the #
# PluginManager. A simple example of registering a plugin is as follows: #
# #
# Scripts must be in .rb files. You should not put any other files into a #
# plugin's folder except for script files and meta.txt. #
# PluginManager.register({ #
# :name => "Basic Plugin", #
# :version => "1.0", #
# :essentials => "20", #
# :link => "https://reliccastle.com/link-to-the-plugin/", #
# :credits => ["Marin"] #
# }) #
# #
# When the game is compiled, scripts in these folders are read and converted #
# into a usable format, and saved in the file Data/PluginScripts.rxdata. #
# Script files are loaded in order of their name and subfolder, so it is wise #
# to name script files "001_first script.rb", "002_second script.rb", etc. to #
# ensure they are loaded in the correct order. #
# The :link value is optional, but recommended. This will be shown in the #
# message if the PluginManager detects that this plugin needs to be updated. #
# #
# When the game is compressed for distribution, the Plugin folder and all its #
# contents should be deleted (like the PBS folder), because its contents will #
# be unused (they will have been compiled into the PluginScripts.rxdata file). #
# Here is the same example but also with dependencies and conflicts: #
# #
# The contents of meta.txt are as follows: #
# PluginManager.register({ #
# :name => "Basic Plugin", #
# :version => "1.0", #
# :essentials => "20", #
# :link => "https://reliccastle.com/link-to-the-plugin/", #
# :credits => ["Marin"], #
# :dependencies => ["Basic Plugin", #
# ["Useful Utils", "1.1"], #
# [:exact, "Scene Tweaks", "2"], #
# [:optional, "Extended Windows", "1.2"], #
# ], #
# :incompatibilities => ["Simple Extension"] #
# }) #
# #
# Name = Simple Extension #
# Version = 1.0 #
# Requires = Basic Plugin #
# Requires = Useful Utilities,1.1 #
# Conflicts = Complex Extension #
# Conflicts = Extended Windows #
# Link = https://reliccastle.com/link-to-the-plugin/ #
# Credits = Luka S.J.,Maruno,Marin #
# #
# These lines are related to what is described above. You can have multiple #
# "Requires" and "Conflicts" lines, each listing a single other plugin that is #
# either a dependency or a conflict respectively. #
# #
# Examples of the "Requires" line: #
# #
# Requires = Basic Plugin #
# Requires = Basic Plugin,1.1 #
# Requires = Basic Plugin,1.1,exact #
# Requires = Basic Plugin,1.1,optional #
# Exact = Basic Plugin,1.1 #
# Optional = Basic Plugin,1.1 #
# #
# The "Exact" and "Optional" lines are equivalent to the "Requires" lines #
# that contain those keywords. #
# #
# There is also a "Scripts" line, which lists one or more script files that #
# should be loaded first. You can have multiple "Scripts" lines. However, you #
# can achieve the same effect by simply naming your script files in #
# alphanumeric order to make them load in a particular order, so the "Scripts" #
# line should not be necessary. #
# The example dependencies/conflict are the same as the examples shown above #
# for lines in "meta.txt". :optional_exact is a combination of :exact and #
# :optional, and there is no way to make use of its combined functionality via #
# "meta.txt". #
# #
#------------------------------------------------------------------------------#
# Please give credit when using this. #
#==============================================================================#
module PluginManager
# Holds all registered plugin data.
@@Plugins = {}
#-----------------------------------------------------------------------------
# Registers a plugin and tests its dependencies and incompatibilities.
#-----------------------------------------------------------------------------
def self.register(options)
name = nil
version = nil
essentials = nil
link = nil
dependencies = nil
incompats = nil
credits = []
order = [:name, :version, :link, :dependencies, :incompatibilities, :credits]
order = [:name, :version, :essentials, :link, :dependencies, :incompatibilities, :credits]
# Ensure it first reads the plugin's name, which is used in error reporting,
# by sorting the keys
keys = options.keys.sort do |a, b|
idx_a = order.index(a)
idx_a = order.size if idx_a == -1
idx_b = order.index(b)
idx_b = order.size if idx_b == -1
idx_a = order.index(a) || order.size
idx_b = order.index(b) || order.size
next idx_a <=> idx_b
end
for key in keys
keys.each do |key|
value = options[key]
case key
when :name # Plugin name
@@ -198,10 +135,10 @@ module PluginManager
end
name = value
when :version # Plugin version
if nil_or_empty?(value)
self.error("Plugin version must be a string.")
end
self.error("Plugin version must be a string.") if nil_or_empty?(value)
version = value
when :essentials
essentials = value
when :link # Plugin website
if nil_or_empty?(value)
self.error("Plugin link must be a non-empty string.")
@@ -210,12 +147,13 @@ module PluginManager
when :dependencies # Plugin dependencies
dependencies = value
dependencies = [dependencies] if !dependencies.is_a?(Array) || !dependencies[0].is_a?(Array)
for dep in value
if dep.is_a?(String) # "plugin name"
value.each do |dep|
case dep
when String # "plugin name"
if !self.installed?(dep)
self.error("Plugin '#{name}' requires plugin '#{dep}' to be installed above it.")
end
elsif dep.is_a?(Array)
when Array
case dep.size
when 1 # ["plugin name"]
if dep[0].is_a?(String)
@@ -236,7 +174,8 @@ module PluginManager
if self.installed?(dep_name) # Have plugin but lower version
msg = "Plugin '#{name}' requires plugin '#{dep_name}' version #{dep_version} or higher, " +
"but the installed version is #{self.version(dep_name)}."
if dep_link = self.link(dep_name)
dep_link = self.link(dep_name)
if dep_link
msg += "\r\nCheck #{dep_link} for an update to plugin '#{dep_name}'."
end
self.error(msg)
@@ -278,7 +217,8 @@ module PluginManager
msg = "Plugin '#{name}' requires plugin '#{dep_name}', if installed, to be version #{dep_version}"
msg << " or higher" if !exact
msg << ", but the installed version was #{self.version(dep_name)}."
if dep_link = self.link(dep_name)
dep_link = self.link(dep_name)
if dep_link
msg << "\r\nCheck #{dep_link} for an update to plugin '#{dep_name}'."
end
self.error(msg)
@@ -288,16 +228,16 @@ module PluginManager
msg = "Plugin '#{name}' requires plugin '#{dep_name}' to be version #{dep_version}"
msg << " or later" if !exact
msg << ", but the installed version was #{self.version(dep_name)}."
if dep_link = self.link(dep_name)
dep_link = self.link(dep_name)
if dep_link
msg << "\r\nCheck #{dep_link} for an update to plugin '#{dep_name}'."
end
self.error(msg)
else # Don't have plugin
msg = "Plugin '#{name}' requires plugin '#{dep_name}' version #{dep_version} "
msg << "or later" if !exact
msg << "or later " if !exact
msg << "to be installed above it."
self.error(msg)
end
self.error(msg)
end
end
end
@@ -305,20 +245,19 @@ module PluginManager
when :incompatibilities # Plugin incompatibilities
incompats = value
incompats = [incompats] if !incompats.is_a?(Array)
for incompat in incompats
incompats.each do |incompat|
if self.installed?(incompat)
self.error("Plugin '#{name}' is incompatible with '#{incompat}'. " +
"They cannot both be used at the same time.")
self.error("Plugin '#{name}' is incompatible with '#{incompat}'. They cannot both be used at the same time.")
end
end
when :credits # Plugin credits
value = [value] if value.is_a?(String)
if value.is_a?(Array)
for entry in value
if !entry.is_a?(String)
self.error("Plugin '#{name}'s credits array contains a non-string value.")
else
value.each do |entry|
if entry.is_a?(String)
credits << entry
else
self.error("Plugin '#{name}'s credits array contains a non-string value.")
end
end
else
@@ -328,30 +267,29 @@ module PluginManager
self.error("Invalid plugin registry key '#{key}'.")
end
end
for plugin in @@Plugins.values
if plugin[:incompatibilities] && plugin[:incompatibilities].include?(name)
self.error("Plugin '#{plugin[:name]}' is incompatible with '#{name}'. " +
"They cannot both be used at the same time.")
@@Plugins.each_value do |plugin|
if plugin[:incompatibilities]&.include?(name)
self.error("Plugin '#{plugin[:name]}' is incompatible with '#{name}'. They cannot both be used at the same time.")
end
end
# Add plugin to class variable
@@Plugins[name] = {
:name => name,
:version => version,
:link => link,
:dependencies => dependencies,
:name => name,
:version => version,
:essentials => essentials,
:link => link,
:dependencies => dependencies,
:incompatibilities => incompats,
:credits => credits
:credits => credits
}
end
#-----------------------------------------------------------------------------
# Throws a pure error message without stack trace or any other useless info.
#-----------------------------------------------------------------------------
def self.error(msg)
Graphics.update
t = Thread.new do
echoln "Plugin Error:\r\n#{msg}"
p "Plugin Error: #{msg}"
Console.echo_error("Plugin Error:\r\n#{msg}")
print("Plugin Error:\r\n#{msg}")
Thread.exit
end
while t.status
@@ -359,11 +297,10 @@ module PluginManager
end
Kernel.exit! true
end
#-----------------------------------------------------------------------------
# Returns true if the specified plugin is installed.
# If the version is specified, this version is taken into account.
# If mustequal is true, the version must be a match with the specified version.
#-----------------------------------------------------------------------------
def self.installed?(plugin_name, plugin_version = nil, mustequal = false)
plugin = @@Plugins[plugin_name]
return false if plugin.nil?
@@ -372,87 +309,97 @@ module PluginManager
return true if !mustequal && comparison >= 0
return true if mustequal && comparison == 0
end
#-----------------------------------------------------------------------------
# Returns the string names of all installed plugins.
#-----------------------------------------------------------------------------
def self.plugins
return @@Plugins.keys
end
#-----------------------------------------------------------------------------
# Returns the installed version of the specified plugin.
#-----------------------------------------------------------------------------
def self.version(plugin_name)
return if !installed?(plugin_name)
return @@Plugins[plugin_name][:version]
end
#-----------------------------------------------------------------------------
# Returns the link of the specified plugin.
#-----------------------------------------------------------------------------
def self.link(plugin_name)
return if !installed?(plugin_name)
return @@Plugins[plugin_name][:link]
end
#-----------------------------------------------------------------------------
# Returns the credits of the specified plugin.
#-----------------------------------------------------------------------------
def self.credits(plugin_name)
return if !installed?(plugin_name)
return @@Plugins[plugin_name][:credits]
end
#-----------------------------------------------------------------------------
# Compares two versions given in string form. v1 should be the plugin version
# you actually have, and v2 should be the minimum/desired plugin version.
# Return values:
# 1 if v1 is higher than v2
# 0 if v1 is equal to v2
# -1 if v1 is lower than v2
#-----------------------------------------------------------------------------
def self.compare_versions(v1, v2)
d1 = v1.split("")
d1.insert(0, "0") if d1[0] == "." # Turn ".123" into "0.123"
while d1[-1] == "."; d1 = d1[0..-2]; end # Turn "123." into "123"
d2 = v2.split("")
d2.insert(0, "0") if d2[0] == "." # Turn ".123" into "0.123"
while d2[-1] == "."; d2 = d2[0..-2]; end # Turn "123." into "123"
for i in 0...[d1.size, d2.size].max # Compare each digit in turn
c1 = d1[i]
c2 = d2[i]
if c1
return 1 if !c2
return 1 if c1.to_i(16) > c2.to_i(16)
return -1 if c1.to_i(16) < c2.to_i(16)
else
return -1 if c2
version_chunks1 = v1.split(".")
version_chunks1.each_with_index do |val, i|
next if val != ""
version_chunks1[i] = (i == 0) ? "0" : nil
end
version_chunks1.compact!
version_chunks2 = v2.split(".")
version_chunks2.each_with_index do |val, i|
next if val != ""
version_chunks2[i] = (i == 0) ? "0" : nil
end
version_chunks2.compact!
# Compare each chunk in turn
decision = :equal # Could be :higher or :lower
[version_chunks1.length, version_chunks2.length].max.times do |i|
chunk1 = version_chunks1[i]
chunk2 = version_chunks2[i]
if !chunk1
decision = :lower if decision == :equal
break
elsif !chunk2
decision = :higher if decision == :equal
break
end
# Make both chunks the same left by pre-padding with "0"
chars_count = [chunk1.length, chunk2.length].max
chunk1 = chunk1.rjust(chars_count, "0").chars
chunk2 = chunk2.rjust(chars_count, "0").chars
chunk1.length.times do |j|
c1 = chunk1[j]
c2 = chunk2[j]
next if c1 == c2
decision = (c1.to_i(16) > c2.to_i(16)) ? :higher : :lower
break
end
break if decision != :equal
end
case decision
when :equal then return 0
when :higher then return 1
when :lower then return -1
end
return 0
end
#-----------------------------------------------------------------------------
# formats the error message
#-----------------------------------------------------------------------------
# Formats the error message
def self.pluginErrorMsg(name, script)
e = $!
# begin message formatting
message = "[Pokémon Essentials version #{Essentials::VERSION}]\r\n"
message = "[Pokémon Essentials version #{Essentials::VERSION}]\r\n"
message += "#{Essentials::ERROR_TEXT}\r\n" # For third party scripts to add to
message += "Error in Plugin [#{name}]:\r\n"
message += "#{$!.class} occurred.\r\n"
# go through message content
for line in $!.message.split("\r\n")
next if nil_or_empty?(line)
n = line[/\d+/]
err = line.split(":")[-1].strip
lms = line.split(":")[0].strip
err.gsub!(n, "") if n
err = err.capitalize if err.is_a?(String) && !err.empty?
linum = n ? "Line #{n}: " : ""
message += "#{linum}#{err}: #{lms}\r\n"
end
message += "Error in Plugin: [#{name}]\r\n"
message += "Exception: #{e.class}\r\n"
message += "Message: "
message += e.message
# show last 10 lines of backtrace
message += "\r\nBacktrace:\r\n"
$!.backtrace[0, 10].each { |i| message += "#{i}\r\n" }
message += "\r\n\r\nBacktrace:\r\n"
e.backtrace[0, 10].each { |i| message += "#{i}\r\n" }
# output to log
errorlog = "errorlog.txt"
errorlog = RTP.getSaveFileName("errorlog.txt") if (Object.const_defined?(:RTP) rescue false)
File.open(errorlog, "ab") do |f|
f.write("\r\n=================\r\n\r\n[#{Time.now}]\r\n")
f.write(message)
@@ -465,8 +412,8 @@ module PluginManager
# output message
print("#{message}\r\nThis exception was logged in #{errorlogline}.\r\nHold Ctrl when closing this message to copy it to the clipboard.")
# Give a ~500ms coyote time to start holding Control
t = System.delta
until (System.delta - t) >= 500000
t = System.uptime
until System.uptime - t >= 0.5
Input.update
if Input.press?(Input::CTRL)
Input.clipboard = message
@@ -474,24 +421,26 @@ module PluginManager
end
end
end
#-----------------------------------------------------------------------------
# Used to read the metadata file
#-----------------------------------------------------------------------------
def self.readMeta(dir, file)
filename = "#{dir}/#{file}"
meta = {}
# read file
Compiler.pbCompilerEachPreppedLine(filename) { |line, line_no|
Compiler.pbCompilerEachPreppedLine(filename) do |line, line_no|
# split line up into property name and values
if !line[/^\s*(\w+)\s*=\s*(.*)$/]
raise _INTL("Bad line syntax (expected syntax like XXX=YYY)\r\n{1}", FileLineData.linereport)
raise _INTL("Bad line syntax (expected syntax like XXX=YYY).") + "\n" + FileLineData.linereport
end
property = $~[1].upcase
data = $~[2].split(',')
data = $~[2].split(",")
data.each_with_index { |value, i| data[i] = value.strip }
# begin formatting data hash
case property
when 'REQUIRES'
when "ESSENTIALS"
meta[:essentials] = [] if !meta[:essentials]
data.each { |ver| meta[:essentials].push(ver) }
when "REQUIRES"
meta[:dependencies] = [] if !meta[:dependencies]
if data.length < 2 # No version given, just push name of plugin dependency
meta[:dependencies].push(data[0])
@@ -501,33 +450,33 @@ module PluginManager
else # Push dependency type, name and version of plugin dependency
meta[:dependencies].push([data[2].downcase.to_sym, data[0], data[1]])
end
when 'EXACT'
when "EXACT"
next if data.length < 2 # Exact dependencies must have a version given; ignore if not
meta[:dependencies] = [] if !meta[:dependencies]
meta[:dependencies].push([:exact, data[0], data[1]])
when 'OPTIONAL'
when "OPTIONAL"
next if data.length < 2 # Optional dependencies must have a version given; ignore if not
meta[:dependencies] = [] if !meta[:dependencies]
meta[:dependencies].push([:optional, data[0], data[1]])
when 'CONFLICTS'
when "CONFLICTS"
meta[:incompatibilities] = [] if !meta[:incompatibilities]
data.each { |value| meta[:incompatibilities].push(value) if value && !value.empty? }
when 'SCRIPTS'
when "SCRIPTS"
meta[:scripts] = [] if !meta[:scripts]
data.each { |scr| meta[:scripts].push(scr) }
when 'CREDITS'
when "CREDITS"
meta[:credits] = data
when 'LINK', 'WEBSITE'
when "LINK", "WEBSITE"
meta[:link] = data[0]
else
meta[property.downcase.to_sym] = data[0]
end
}
end
# generate a list of all script files to be loaded, in the order they are to
# be loaded (files listed in the meta file are loaded first)
meta[:scripts] = [] if !meta[:scripts]
# get all script files from plugin Dir
for fl in Dir.all(dir)
Dir.all(dir).each do |fl|
next if !fl.include?(".rb")
meta[:scripts].push(fl.gsub("#{dir}/", ""))
end
@@ -536,26 +485,24 @@ module PluginManager
# return meta hash
return meta
end
#-----------------------------------------------------------------------------
# Get a list of all the plugin directories to inspect
#-----------------------------------------------------------------------------
def self.listAll
return [] if !$DEBUG || safeExists?("Game.rgssad")
return [] if !$DEBUG || FileTest.exist?("Game.rgssad") || !Dir.safe?("Plugins")
# get a list of all directories in the `Plugins/` folder
dirs = []
Dir.get("Plugins").each { |d| dirs.push(d) if Dir.safe?(d) }
# return all plugins
return dirs
end
#-----------------------------------------------------------------------------
# Catch any potential loop with dependencies and raise an error
#-----------------------------------------------------------------------------
def self.validateDependencies(name, meta, og = nil)
# exit if no registered dependency
return nil if !meta[name] || !meta[name][:dependencies]
og = [name] if !og
# go through all dependencies
for dname in meta[name][:dependencies]
meta[name][:dependencies].each do |dname|
# clean the name to a simple string
dname = dname[0] if dname.is_a?(Array) && dname.length == 2
dname = dname[1] if dname.is_a?(Array) && dname.length == 3
@@ -567,20 +514,25 @@ module PluginManager
end
return name
end
#-----------------------------------------------------------------------------
# Sort load order based on dependencies (this ends up in reverse order)
#-----------------------------------------------------------------------------
def self.sortLoadOrder(order, plugins)
# go through the load order
for o in order
order.each do |o|
next if !plugins[o] || !plugins[o][:dependencies]
# go through all dependencies
for dname in plugins[o][:dependencies]
plugins[o][:dependencies].each do |dname|
optional = false
# clean the name to a simple string
dname = dname[0] if dname.is_a?(Array) && dname.length == 2
dname = dname[1] if dname.is_a?(Array) && dname.length == 3
if dname.is_a?(Array)
optional = [:optional, :optional_exact].include?(dname[0])
dname = dname[dname.length - 2]
end
# catch missing dependency
self.error("Plugin '#{o}' requires plugin '#{dname}' to work properly.") if !order.include?(dname)
if !order.include?(dname)
next if optional
self.error("Plugin '#{o}' requires plugin '#{dname}' to work properly.")
end
# skip if already sorted
next if order.index(dname) > order.index(o)
# catch looping dependency issue
@@ -590,17 +542,16 @@ module PluginManager
end
return order
end
#-----------------------------------------------------------------------------
# Get the order in which to load plugins
#-----------------------------------------------------------------------------
def self.getPluginOrder
plugins = {}
order = []
# Find all plugin folders that have a meta.txt and add them to the list of
# plugins.
for dir in self.listAll
self.listAll.each do |dir|
# skip if there is no meta file
next if !safeExists?(dir + "/meta.txt")
next if !FileTest.exist?(dir + "/meta.txt")
ndx = order.length
meta = self.readMeta(dir, "meta.txt")
meta[:dir] = dir
@@ -618,44 +569,53 @@ module PluginManager
# sort the load order
return self.sortLoadOrder(order, plugins).reverse, plugins
end
#-----------------------------------------------------------------------------
# Check if plugins need compiling
#-----------------------------------------------------------------------------
def self.needCompiling?(order, plugins)
# fixed actions
return false if !$DEBUG || safeExists?("Game.rgssad")
return true if !safeExists?("Data/PluginScripts.rxdata")
return false if !$DEBUG || FileTest.exist?("Game.rgssad")
return true if $full_compile
return true if !FileTest.exist?("Data/PluginScripts.rxdata")
Input.update
return true if Input.press?(Input::CTRL)
# analyze whether or not to push recompile
# Force compiling if holding Shift or Ctrl
return true if Input.press?(Input::SHIFT) || Input.press?(Input::CTRL)
# Should compile if the number of plugins has changed
scripts = load_data("Data/PluginScripts.rxdata")
return true if scripts.length != plugins.length
# Should compile if any plugins have changed version or been replaced
found_plugins = []
plugins.each_pair { |name, meta| found_plugins.push([meta[:name], meta[:version]]) }
existing_plugins = []
scripts.each { |plugin| existing_plugins.push([plugin[1][:name], plugin[1][:version]]) }
return true if found_plugins != existing_plugins
# Should compile if any plugin files have been recently modified
mtime = File.mtime("Data/PluginScripts.rxdata")
for o in order
order.each do |o|
# go through all the registered plugin scripts
scr = plugins[o][:scripts]
dir = plugins[o][:dir]
for sc in scr
scr.each do |sc|
return true if File.mtime("#{dir}/#{sc}") > mtime
end
return true if File.mtime("#{dir}/meta.txt") > mtime
end
return false
end
#-----------------------------------------------------------------------------
# Check if plugins need compiling
#-----------------------------------------------------------------------------
def self.compilePlugins(order, plugins)
echo 'Compiling plugin scripts...'
Console.echo_li("Compiling plugin scripts...")
scripts = []
# go through the entire order one by one
for o in order
order.each do |o|
# save name, metadata and scripts array
meta = plugins[o].clone
meta.delete(:scripts)
meta.delete(:dir)
dat = [o, meta, []]
# iterate through each file to deflate
for file in plugins[o][:scripts]
File.open("#{plugins[o][:dir]}/#{file}", 'rb') do |f|
plugins[o][:scripts].each do |file|
File.open("#{plugins[o][:dir]}/#{file}", "rb") do |f|
dat[2].push([file, Zlib::Deflate.deflate(f.read)])
end
end
@@ -663,41 +623,47 @@ module PluginManager
scripts.push(dat)
end
# save to main `PluginScripts.rxdata` file
File.open("Data/PluginScripts.rxdata", 'wb') { |f| Marshal.dump(scripts, f) }
File.open("Data/PluginScripts.rxdata", "wb") { |f| Marshal.dump(scripts, f) }
# collect garbage
GC.start
echoln ' done.'
echoln ''
Console.echo_done(true)
end
#-----------------------------------------------------------------------------
# Check if plugins need compiling
#-----------------------------------------------------------------------------
def self.runPlugins
Console.echo_h1("Checking plugins")
# get the order of plugins to interpret
order, plugins = self.getPluginOrder
# compile if necessary
self.compilePlugins(order, plugins) if self.needCompiling?(order, plugins)
if self.needCompiling?(order, plugins)
self.compilePlugins(order, plugins)
else
Console.echoln_li("Plugins were not compiled")
end
# load plugins
scripts = load_data("Data/PluginScripts.rxdata")
echoed_plugins = []
for plugin in scripts
scripts.each do |plugin|
# get the required data
name, meta, script = plugin
if !meta[:essentials] || !meta[:essentials].include?(Essentials::VERSION)
Console.echo_warn("Plugin '#{name}' may not be compatible with Essentials v#{Essentials::VERSION}. Trying to load anyway.")
end
# register plugin
self.register(meta)
# go through each script and interpret
for scr in script
script.each do |scr|
# turn code into plaintext
code = Zlib::Inflate.inflate(scr[1]).force_encoding(Encoding::UTF_8)
# get rid of tabs
code.gsub!("\t", " ")
# construct filename
sname = scr[0].gsub("\\","/").split("/")[-1]
sname = scr[0].gsub("\\", "/").split("/")[-1]
fname = "[#{name}] #{sname}"
# try to run the code
begin
eval(code, TOPLEVEL_BINDING, fname)
echoln "Loaded plugin: #{name}" if !echoed_plugins.include?(name)
Console.echoln_li("Loaded plugin: ==#{name}== (ver. #{meta[:version]})") if !echoed_plugins.include?(name)
echoed_plugins.push(name)
rescue Exception # format error message to display
self.pluginErrorMsg(name, sname)
@@ -705,7 +671,24 @@ module PluginManager
end
end
end
echoln '' if !echoed_plugins.empty?
if scripts.length > 0
Console.echoln_li_done("Successfully loaded #{scripts.length} plugin(s)")
else
Console.echoln_li_done("No plugins found")
end
end
# Get plugin dir from name based on meta entries
def self.findDirectory(name)
# go through the plugins folder
Dir.get("Plugins").each do |dir|
next if !Dir.safe?(dir)
next if !FileTest.exist?(dir + "/meta.txt")
# read meta
meta = self.readMeta(dir, "meta.txt")
return dir if meta[:name] == name
end
# return nil if no plugin dir found
return nil
end
#-----------------------------------------------------------------------------
end

View File

@@ -1,534 +1,103 @@
class SpriteAnimation
@@_animations = []
@@_reference_count = {}
def initialize(sprite)
@sprite = sprite
end
%w[
x y ox oy viewport flash src_rect opacity tone
].each_with_index do |s, _i|
eval <<-__END__
def #{s}(*arg)
@sprite.#{s}(*arg)
end
__END__
end
def self.clear
@@_animations.clear
end
def dispose
dispose_animation
dispose_loop_animation
end
def animation(animation, hit, height = 3)
dispose_animation
@_animation = animation
return if @_animation == nil
@_animation_hit = hit
@_animation_height = height
@_animation_duration = @_animation.frame_max
fr = 20
if @_animation.name[/\[\s*(\d+?)\s*\]\s*$/]
fr = $~[1].to_i
end
@_animation_frame_skip = Graphics.frame_rate / fr
animation_name = @_animation.animation_name
animation_hue = @_animation.animation_hue
bitmap = pbGetAnimation(animation_name, animation_hue)
if @@_reference_count.include?(bitmap)
@@_reference_count[bitmap] += 1
else
@@_reference_count[bitmap] = 1
end
@_animation_sprites = []
if @_animation.position != 3 || !@@_animations.include?(animation)
16.times do
sprite = ::Sprite.new(self.viewport)
sprite.bitmap = bitmap
sprite.visible = false
@_animation_sprites.push(sprite)
end
unless @@_animations.include?(animation)
@@_animations.push(animation)
end
end
update_animation
end
def loop_animation(animation)
return if animation == @_loop_animation
dispose_loop_animation
@_loop_animation = animation
return if @_loop_animation == nil
@_loop_animation_index = 0
fr = 20
if @_animation.name[/\[\s*(\d+?)\s*\]\s*$/]
fr = $~[1].to_i
end
@_loop_animation_frame_skip = Graphics.frame_rate / fr
animation_name = @_loop_animation.animation_name
animation_hue = @_loop_animation.animation_hue
bitmap = pbGetAnimation(animation_name, animation_hue)
if @@_reference_count.include?(bitmap)
@@_reference_count[bitmap] += 1
else
@@_reference_count[bitmap] = 1
end
@_loop_animation_sprites = []
16.times do
sprite = ::Sprite.new(self.viewport)
sprite.bitmap = bitmap
sprite.visible = false
@_loop_animation_sprites.push(sprite)
end
update_loop_animation
end
def dispose_animation
return if @_animation_sprites == nil
sprite = @_animation_sprites[0]
if sprite != nil
@@_reference_count[sprite.bitmap] -= 1
if @@_reference_count[sprite.bitmap] == 0
sprite.bitmap.dispose
end
end
for sprite in @_animation_sprites
sprite.dispose
end
@_animation_sprites = nil
@_animation = nil
end
def dispose_loop_animation
return if @_loop_animation_sprites == nil
sprite = @_loop_animation_sprites[0]
if sprite != nil
@@_reference_count[sprite.bitmap] -= 1
if @@_reference_count[sprite.bitmap] == 0
sprite.bitmap.dispose
end
end
for sprite in @_loop_animation_sprites
sprite.dispose
end
@_loop_animation_sprites = nil
@_loop_animation = nil
end
def active?
return @_loop_animation_sprites != nil || @_animation_sprites != nil
end
def effect?
return @_animation_duration > 0
end
def update
if @_animation != nil
quick_update = true
if Graphics.frame_count % @_animation_frame_skip == 0
@_animation_duration -= 1
quick_update = false
end
update_animation(quick_update)
end
if @_loop_animation != nil
quick_update = (Graphics.frame_count % @_loop_animation_frame_skip != 0)
update_loop_animation(quick_update)
if !quick_update
@_loop_animation_index += 1
@_loop_animation_index %= @_loop_animation.frame_max
end
end
end
def update_animation(quick_update = false)
if @_animation_duration <= 0
dispose_animation
return
end
frame_index = @_animation.frame_max - @_animation_duration
cell_data = @_animation.frames[frame_index].cell_data
position = @_animation.position
animation_set_sprites(@_animation_sprites, cell_data, position, quick_update)
return if quick_update
for timing in @_animation.timings
next if timing.frame != frame_index
animation_process_timing(timing, @_animation_hit)
end
end
def update_loop_animation(quick_update = false)
frame_index = @_loop_animation_index
cell_data = @_loop_animation.frames[frame_index].cell_data
position = @_loop_animation.position
animation_set_sprites(@_loop_animation_sprites, cell_data, position, quick_update)
return if quick_update
for timing in @_loop_animation.timings
next if timing.frame != frame_index
animation_process_timing(timing, true)
end
end
def animation_set_sprites(sprites, cell_data, position, quick_update = false)
sprite_x = 320
sprite_y = 240
if position == 3
if self.viewport != nil
sprite_x = self.viewport.rect.width / 2
sprite_y = self.viewport.rect.height - 160
end
else
sprite_x = self.x - self.ox + self.src_rect.width / 2
sprite_y = self.y - self.oy
sprite_y += self.src_rect.height / 2 if position == 1
sprite_y += self.src_rect.height if position == 2
end
for i in 0..15
sprite = sprites[i]
pattern = cell_data[i, 0]
if sprite == nil || pattern == nil || pattern == -1
sprite.visible = false if sprite != nil
next
end
sprite.x = sprite_x + cell_data[i, 1]
sprite.y = sprite_y + cell_data[i, 2]
next if quick_update
sprite.visible = true
sprite.src_rect.set(pattern % 5 * 192, pattern / 5 * 192, 192, 192)
case @_animation_height
when 0 then sprite.z = 1
when 1 then sprite.z = sprite.y+32+15
when 2 then sprite.z = sprite.y+32+32+17
else sprite.z = 2000
end
sprite.ox = 96
sprite.oy = 96
sprite.zoom_x = cell_data[i, 3] / 100.0
sprite.zoom_y = cell_data[i, 3] / 100.0
sprite.angle = cell_data[i, 4]
sprite.mirror = (cell_data[i, 5] == 1)
sprite.tone = self.tone
sprite.opacity = cell_data[i, 6] * self.opacity / 255.0
sprite.blend_type = cell_data[i, 7]
end
end
def animation_process_timing(timing, hit)
if timing.condition == 0 ||
(timing.condition == 1 && hit == true) ||
(timing.condition == 2 && hit == false)
if timing.se.name != ""
se = timing.se
pbSEPlay(se)
end
case timing.flash_scope
when 1
self.flash(timing.flash_color, timing.flash_duration * 2)
when 2
if self.viewport != nil
self.viewport.flash(timing.flash_color, timing.flash_duration * 2)
end
when 3
self.flash(nil, timing.flash_duration * 2)
end
end
end
def x=(x)
sx = x - self.x
return if sx == 0
if @_animation_sprites != nil
for i in 0..15
@_animation_sprites[i].x += sx
end
end
if @_loop_animation_sprites != nil
for i in 0..15
@_loop_animation_sprites[i].x += sx
end
end
end
def y=(y)
sy = y - self.y
return if sy == 0
if @_animation_sprites != nil
for i in 0..15
@_animation_sprites[i].y += sy
end
end
if @_loop_animation_sprites != nil
for i in 0..15
@_loop_animation_sprites[i].y += sy
end
end
end
end
#===============================================================================
# Additions to class Sprite that allows class AnimationContainerSprite to attach
# overworld animations to itself.
#===============================================================================
module RPG
class Sprite < ::Sprite
def initialize(viewport = nil)
super(viewport)
@_whiten_duration = 0
@_appear_duration = 0
@_escape_duration = 0
@_collapse_duration = 0
@_damage_duration = 0
@_animation_duration = 0
@_blink = false
@animations = []
@_animation_frame = 0
@animations = []
@loopAnimations = []
end
def dispose
dispose_damage
dispose_animation
dispose_loop_animation
super
end
def whiten
self.blend_type = 0
self.color.set(255, 255, 255, 128)
self.opacity = 255
@_whiten_duration = 16
@_appear_duration = 0
@_escape_duration = 0
@_collapse_duration = 0
def dispose_animation
@animations.each { |a| a&.dispose_animation }
@animations.clear
end
def appear
self.blend_type = 0
self.color.set(0, 0, 0, 0)
self.opacity = 0
@_appear_duration = 16
@_whiten_duration = 0
@_escape_duration = 0
@_collapse_duration = 0
def dispose_loop_animation
@loopAnimations.each { |a| a&.dispose_loop_animation }
@loopAnimations.clear
end
def escape
self.blend_type = 0
self.color.set(0, 0, 0, 0)
self.opacity = 255
@_escape_duration = 32
@_whiten_duration = 0
@_appear_duration = 0
@_collapse_duration = 0
def x=(x)
@animations.each { |a| a.x = x if a }
@loopAnimations.each { |a| a.x = x if a }
super
end
def collapse
self.blend_type = 1
self.color.set(255, 64, 64, 255)
self.opacity = 255
@_collapse_duration = 48
@_whiten_duration = 0
@_appear_duration = 0
@_escape_duration = 0
end
def damage(value, critical)
dispose_damage
damage_string = (value.is_a?(Numeric)) ? value.abs.to_s : value.to_s
bitmap = Bitmap.new(160, 48)
bitmap.font.name = "Arial Black"
bitmap.font.size = 32
bitmap.font.color.set(0, 0, 0)
bitmap.draw_text(-1, 12-1, 160, 36, damage_string, 1)
bitmap.draw_text(+1, 12-1, 160, 36, damage_string, 1)
bitmap.draw_text(-1, 12+1, 160, 36, damage_string, 1)
bitmap.draw_text(+1, 12+1, 160, 36, damage_string, 1)
if value.is_a?(Numeric) && value < 0
bitmap.font.color.set(176, 255, 144)
else
bitmap.font.color.set(255, 255, 255)
end
bitmap.draw_text(0, 12, 160, 36, damage_string, 1)
if critical
bitmap.font.size = 20
bitmap.font.color.set(0, 0, 0)
bitmap.draw_text(-1, -1, 160, 20, "CRITICAL", 1)
bitmap.draw_text(+1, -1, 160, 20, "CRITICAL", 1)
bitmap.draw_text(-1, +1, 160, 20, "CRITICAL", 1)
bitmap.draw_text(+1, +1, 160, 20, "CRITICAL", 1)
bitmap.font.color.set(255, 255, 255)
bitmap.draw_text(0, 0, 160, 20, "CRITICAL", 1)
end
@_damage_sprite = ::Sprite.new(self.viewport)
@_damage_sprite.bitmap = bitmap
@_damage_sprite.ox = 80
@_damage_sprite.oy = 20
@_damage_sprite.x = self.x
@_damage_sprite.y = self.y - self.oy / 2
@_damage_sprite.z = 3000
@_damage_duration = 40
def y=(y)
@animations.each { |a| a.y = y if a }
@loopAnimations.each { |a| a.y = y if a }
super
end
def pushAnimation(array, anim)
for i in 0...array.length
next if array[i] && array[i].active?
array.length.times do |i|
next if array[i]&.active?
array[i] = anim
return
end
array.push(anim)
end
def animation(animation, hit, height = 3)
def animation(animation, hit, height = 3, no_tone = false)
anim = SpriteAnimation.new(self)
anim.animation(animation,hit,height)
pushAnimation(@animations,anim)
anim.animation(animation, hit, height, no_tone)
pushAnimation(@animations, anim)
end
def loop_animation(animation)
anim = SpriteAnimation.new(self)
anim.loop_animation(animation)
pushAnimation(@loopAnimations,anim)
end
def dispose_damage
return if @_damage_sprite == nil
@_damage_sprite.bitmap.dispose
@_damage_sprite.dispose
@_damage_sprite = nil
@_damage_duration = 0
end
def dispose_animation
for a in @animations
a.dispose_animation if a
end
@animations.clear
end
def dispose_loop_animation
for a in @loopAnimations
a.dispose_loop_animation if a
end
@loopAnimations.clear
end
def blink_on
return if @_blink
@_blink = true
@_blink_count = 0
end
def blink_off
return unless @_blink
@_blink = false
self.color.set(0, 0, 0, 0)
end
def blink?
return @_blink
pushAnimation(@loopAnimations, anim)
end
def effect?
return true if @_whiten_duration > 0
return true if @_appear_duration > 0
return true if @_escape_duration > 0
return true if @_collapse_duration > 0
return true if @_damage_duration > 0
for a in @animations
return true if a.effect?
end
@animations.each { |a| return true if a.effect? }
return false
end
def update_animation
@animations.each { |a| a.update_animation if a&.active? }
end
def update_loop_animation
@loopAnimations.each { |a| a.update_loop_animation if a&.active? }
end
def update
super
if @_whiten_duration > 0
@_whiten_duration -= 1
self.color.alpha = 128 - (16 - @_whiten_duration) * 10
end
if @_appear_duration > 0
@_appear_duration -= 1
self.opacity = (16 - @_appear_duration) * 16
end
if @_escape_duration > 0
@_escape_duration -= 1
self.opacity = 256 - (32 - @_escape_duration) * 10
end
if @_collapse_duration > 0
@_collapse_duration -= 1
self.opacity = 256 - (48 - @_collapse_duration) * 6
end
if @_damage_duration > 0
@_damage_duration -= 1
case @_damage_duration
when 38..39
@_damage_sprite.y -= 4
when 36..37
@_damage_sprite.y -= 2
when 34..35
@_damage_sprite.y += 2
when 28..33
@_damage_sprite.y += 4
end
@_damage_sprite.opacity = 256 - (12 - @_damage_duration) * 32
if @_damage_duration == 0
dispose_damage
end
end
for a in @animations
a.update
end
for a in @loopAnimations
a.update
end
if @_blink
@_blink_count = (@_blink_count + 1) % 32
if @_blink_count < 16
alpha = (16 - @_blink_count) * 6
else
alpha = (@_blink_count - 16) * 6
end
self.color.set(255, 255, 255, alpha)
end
@animations.each { |a| a.update }
@loopAnimations.each { |a| a.update }
SpriteAnimation.clear
end
def update_animation
for a in @animations
a.update_animation if a && a.active?
end
end
def update_loop_animation
for a in @loopAnimations
a.update_loop_animation if a && a.active?
end
end
def x=(x)
for a in @animations
a.x = x if a
end
for a in @loopAnimations
a.x = x if a
end
super
end
def y=(y)
for a in @animations
a.y = y if a
end
for a in @loopAnimations
a.y = y if a
end
super
end
end
end
#===============================================================================
# A version of class Sprite that allows its coordinates to be floats rather than
# integers.
#===============================================================================
class FloatSprite < Sprite
def x; return @float_x; end
def y; return @float_y; end
def x=(value)
@float_x = value
super
end
def y=(value)
@float_y = value
super
end
end

View File

@@ -1,39 +1,49 @@
#===============================================================================
#
#===============================================================================
module Settings
# Whether a move's physical/special category depends on the move itself as in
# newer Gens (true), or on its type as in older Gens (false).
MOVE_CATEGORY_PER_MOVE = (MECHANICS_GENERATION >= 4)
#-----------------------------------------------------------------------------
# Turn order and disobedience
#-----------------------------------------------------------------------------
# Whether turn order is recalculated after a Pokémon Mega Evolves.
RECALCULATE_TURN_ORDER_AFTER_MEGA_EVOLUTION = (MECHANICS_GENERATION >= 7)
# Whether turn order is recalculated after a Pokémon's Speed stat changes.
RECALCULATE_TURN_ORDER_AFTER_SPEED_CHANGES = (MECHANICS_GENERATION >= 8)
# Whether any Pokémon (originally owned by the player or foreign) can disobey
# the player's commands if the Pokémon is too high a level compared to the
# number of Gym Badges the player has.
ANY_HIGH_LEVEL_POKEMON_CAN_DISOBEY = false
# Whether foreign Pokémon can disobey the player's commands if the Pokémon is
# too high a level compared to the number of Gym Badges the player has.
FOREIGN_HIGH_LEVEL_POKEMON_CAN_DISOBEY = true
#-----------------------------------------------------------------------------
# Mega Evolution
#-----------------------------------------------------------------------------
# The Game Switch which, while ON, prevents all Pokémon in battle from Mega
# Evolving even if they otherwise could.
NO_MEGA_EVOLUTION = 34
#-----------------------------------------------------------------------------
# Move usage calculations
#-----------------------------------------------------------------------------
# Whether a move's physical/special category depends on the move itself as in
# newer Gens (true), or on its type as in older Gens (false).
MOVE_CATEGORY_PER_MOVE = (MECHANICS_GENERATION >= 4)
# Whether critical hits do 1.5x damage and have 4 stages (true), or they do 2x
# damage and have 5 stages as in Gen 5 (false). Also determines whether
# critical hit rate can be copied by Transform/Psych Up.
NEW_CRITICAL_HIT_RATE_MECHANICS = (MECHANICS_GENERATION >= 6)
NEW_CRITICAL_HIT_RATE_MECHANICS = (MECHANICS_GENERATION >= 6)
# Whether several effects apply relating to a Pokémon's type:
# * Electric-type immunity to paralysis
# * Ghost-type immunity to being trapped
# * Grass-type immunity to powder moves and Effect Spore
# * Poison-type Pokémon can't miss when using Toxic
MORE_TYPE_EFFECTS = (MECHANICS_GENERATION >= 6)
# Whether weather caused by an ability lasts 5 rounds (true) or forever (false).
FIXED_DURATION_WEATHER_FROM_ABILITY = (MECHANICS_GENERATION >= 6)
#=============================================================================
# Whether X items (X Attack, etc.) raise their stat by 2 stages (true) or 1
# (false).
X_STAT_ITEMS_RAISE_BY_TWO_STAGES = (MECHANICS_GENERATION >= 7)
# Whether some Poké Balls have catch rate multipliers from Gen 7 (true) or
# from earlier generations (false).
NEW_POKE_BALL_CATCH_RATES = (MECHANICS_GENERATION >= 7)
# Whether Soul Dew powers up Psychic and Dragon-type moves by 20% (true) or
# raises the holder's Special Attack and Special Defense by 50% (false).
SOUL_DEW_POWERS_UP_TYPES = (MECHANICS_GENERATION >= 7)
#=============================================================================
# The minimum number of badges required to boost each stat of a player's
MORE_TYPE_EFFECTS = (MECHANICS_GENERATION >= 6)
# The minimum number of Gym Badges required to boost each stat of a player's
# Pokémon by 1.1x, in battle only.
NUM_BADGES_BOOST_ATTACK = (MECHANICS_GENERATION >= 4) ? 999 : 1
NUM_BADGES_BOOST_DEFENSE = (MECHANICS_GENERATION >= 4) ? 999 : 5
@@ -41,38 +51,94 @@ module Settings
NUM_BADGES_BOOST_SPDEF = (MECHANICS_GENERATION >= 4) ? 999 : 7
NUM_BADGES_BOOST_SPEED = (MECHANICS_GENERATION >= 4) ? 999 : 3
#=============================================================================
#-----------------------------------------------------------------------------
# Move, ability and item effects
#-----------------------------------------------------------------------------
# An array of items which act as Mega Rings for the player (NPCs don't need a
# Mega Ring item, just a Mega Stone held by their Pokémon).
MEGA_RINGS = [:MEGARING, :MEGABRACELET, :MEGACUFF, :MEGACHARM]
# The Game Switch which, while ON, prevents all Pokémon in battle from Mega
# Evolving even if they otherwise could.
NO_MEGA_EVOLUTION = 34
# Whether the in-battle hail weather is replaced by Snowstorm (from Gen 9+)
# instead.
USE_SNOWSTORM_WEATHER_INSTEAD_OF_HAIL = (MECHANICS_GENERATION >= 9)
# Whether weather caused by an ability lasts 5 rounds (true) or forever (false).
FIXED_DURATION_WEATHER_FROM_ABILITY = (MECHANICS_GENERATION >= 6)
# Whether X items (X Attack, etc.) raise their stat by 2 stages (true) or 1
# (false).
X_STAT_ITEMS_RAISE_BY_TWO_STAGES = (MECHANICS_GENERATION >= 7)
# Whether some Poké Balls have catch rate multipliers from Gen 7 (true) or
# from earlier generations (false).
NEW_POKE_BALL_CATCH_RATES = (MECHANICS_GENERATION >= 7)
# Whether Soul Dew powers up Psychic and Dragon-type moves by 20% (true) or
# raises the holder's Special Attack and Special Defense by 50% (false).
SOUL_DEW_POWERS_UP_TYPES = (MECHANICS_GENERATION >= 7)
#=============================================================================
#-----------------------------------------------------------------------------
# Affection
#-----------------------------------------------------------------------------
# Whether Pokémon with high happiness will gain more Exp from battles, have a
# chance of avoiding/curing negative effects by themselves, resisting
# fainting, etc.
AFFECTION_EFFECTS = false
# Whether a Pokémon's happiness is limited to 179, and can only be increased
# further with friendship-raising berries. Related to AFFECTION_EFFECTS by
# default because affection effects only start applying above a happiness of
# 179. Also lowers the happiness evolution threshold to 160.
APPLY_HAPPINESS_SOFT_CAP = AFFECTION_EFFECTS
#-----------------------------------------------------------------------------
# Capturing Pokémon
#-----------------------------------------------------------------------------
# Whether the Exp gained from beating a Pokémon should be scaled depending on
# the gainer's level.
SCALED_EXP_FORMULA = (MECHANICS_GENERATION == 5 || MECHANICS_GENERATION >= 7)
# Whether the Exp gained from beating a Pokémon should be divided equally
# between each participant (true), or whether each participant should gain
# that much Exp (false). This also applies to Exp gained via the Exp Share
# (held item version) being distributed to all Exp Share holders.
SPLIT_EXP_BETWEEN_GAINERS = (MECHANICS_GENERATION <= 5)
# Whether the critical capture mechanic applies. Note that its calculation is
# based on a total of 600+ species (i.e. that many species need to be caught
# to provide the greatest critical capture chance of 2.5x), and there may be
# fewer species in your game.
ENABLE_CRITICAL_CAPTURES = (MECHANICS_GENERATION >= 5)
ENABLE_CRITICAL_CAPTURES = (MECHANICS_GENERATION >= 5)
# Whether the player is asked what to do with a newly caught Pokémon if their
# party is full. If true, the player can toggle whether they are asked this in
# the Options screen.
NEW_CAPTURE_CAN_REPLACE_PARTY_MEMBER = (MECHANICS_GENERATION >= 7)
#-----------------------------------------------------------------------------
# Exp and EV gain
#-----------------------------------------------------------------------------
# Whether the Exp gained from beating a Pokémon should be scaled depending on
# the gainer's level.
SCALED_EXP_FORMULA = (MECHANICS_GENERATION == 5 || MECHANICS_GENERATION >= 7)
# Whether the Exp gained from beating a Pokémon should be divided equally
# between each participant (true), or whether each participant should gain
# that much Exp (false). This also applies to Exp gained via the Exp Share
# (held item version) being distributed to all Exp Share holders.
SPLIT_EXP_BETWEEN_GAINERS = (MECHANICS_GENERATION <= 5)
# Whether the Exp gained from beating a Pokémon is multiplied by 1.5 if that
# Pokémon is owned by another trainer.
MORE_EXP_FROM_TRAINER_POKEMON = (MECHANICS_GENERATION <= 6)
# Whether a Pokémon holding a Power item gains 8 (true) or 4 (false) EVs in
# the relevant stat.
MORE_EVS_FROM_POWER_ITEMS = (MECHANICS_GENERATION >= 7)
# Whether Pokémon gain Exp for capturing a Pokémon.
GAIN_EXP_FOR_CAPTURE = (MECHANICS_GENERATION >= 6)
# The Game Switch which, whie ON, prevents the player from losing money if
GAIN_EXP_FOR_CAPTURE = (MECHANICS_GENERATION >= 6)
#-----------------------------------------------------------------------------
# End of battle
#-----------------------------------------------------------------------------
CAN_FORFEIT_TRAINER_BATTLES = (MECHANICS_GENERATION >= 9)
# The Game Switch which, while ON, prevents the player from losing money if
# they lose a battle (they can still gain money from trainers for winning).
NO_MONEY_LOSS = 33
NO_MONEY_LOSS = 33
# Whether party Pokémon check whether they can evolve after all battles
# regardless of the outcome (true), or only after battles the player won (false).
CHECK_EVOLUTION_AFTER_ALL_BATTLES = (MECHANICS_GENERATION >= 6)
# Whether fainted Pokémon can try to evolve after a battle.
CHECK_EVOLUTION_FOR_FAINTED_POKEMON = true
#-----------------------------------------------------------------------------
# AI
#-----------------------------------------------------------------------------
# Whether wild Pokémon with the "Legendary", "Mythical" or "UltraBeast" flag
# (as defined in pokemon.txt) have a smarter AI. Their skill level is set to
# 32, which is a medium skill level.
SMARTER_WILD_LEGENDARY_POKEMON = true
end

View File

@@ -1,19 +1,30 @@
#===============================================================================
# The SaveData module is used to manipulate save data. It contains the {Value}s
# that make up the save data and {Conversion}s for resolving incompatibilities
# between Essentials and game versions.
# @see SaveData.register
# @see SaveData.register_conversion
#===============================================================================
module SaveData
# Contains the file path of the save file.
FILE_PATH = if File.directory?(System.data_directory)
System.data_directory + '/Game.rxdata'
else
'./Game.rxdata'
end
DIRECTORY = (File.directory?(System.data_directory)) ? System.data_directory : "./"
FILENAME_REGEX = /Game(\d*)\.rxdata$/
# @return [Boolean] whether the save file exists
# @return [Boolean] whether any save files exist
def self.exists?
return File.file?(FILE_PATH)
return !all_save_files.empty?
end
# @return[Array] array of filenames in the save folder that are save files
def self.all_save_files
files = Dir.get(DIRECTORY, "*", false)
ret = []
files.each do |file|
next if !file[FILENAME_REGEX]
ret.push([$~[1].to_i, file])
end
ret.sort! { |a, b| a[0] <=> b[0] }
ret.map! { |val| val[1] }
return ret
end
# Fetches the save data from the given file.
@@ -43,9 +54,9 @@ module SaveData
def self.read_from_file(file_path)
validate file_path => String
save_data = get_data_from_file(file_path)
save_data = to_hash_format(save_data) if save_data.is_a?(Array)
save_data = to_hash_format(save_data) if save_data.is_a?(Array) # Pre-v19 save file support
if !save_data.empty? && run_conversions(save_data)
File.open(file_path, 'wb') { |file| Marshal.dump(save_data, file) }
File.open(file_path, "wb") { |file| Marshal.dump(save_data, file) }
end
return save_data
end
@@ -57,14 +68,19 @@ module SaveData
def self.save_to_file(file_path)
validate file_path => String
save_data = self.compile_save_hash
File.open(file_path, 'wb') { |file| Marshal.dump(save_data, file) }
File.open(file_path, "wb") { |file| Marshal.dump(save_data, file) }
end
# Deletes the save file (and a possible .bak backup file if one exists)
# @raise [Error::ENOENT]
def self.delete_file
File.delete(FILE_PATH)
File.delete(FILE_PATH + '.bak') if File.file?(FILE_PATH + '.bak')
def self.delete_file(filename)
File.delete(DIRECTORY + filename)
File.delete(DIRECTORY + filename + ".bak") if File.file?(DIRECTORY + filename + ".bak")
end
def self.filename_from_index(index = 0)
return "Game.rxdata" if index <= 0
return "Game#{index}.rxdata"
end
# Converts the pre-v19 format data to the new format.
@@ -79,19 +95,4 @@ module SaveData
end
return hash
end
# Moves a save file from the old Saved Games folder to the new
# location specified by {FILE_PATH}. Does nothing if a save file
# already exists in {FILE_PATH}.
def self.move_old_windows_save
return if File.file?(FILE_PATH)
game_title = System.game_title.gsub(/[^\w ]/, '_')
home = ENV['HOME'] || ENV['HOMEPATH']
return if home.nil?
old_location = File.join(home, 'Saved Games', game_title)
return unless File.directory?(old_location)
old_file = File.join(old_location, 'Game.rxdata')
return unless File.file?(old_file)
File.move(old_file, FILE_PATH)
end
end

View File

@@ -1,15 +1,21 @@
#===============================================================================
#
#===============================================================================
module SaveData
# Contains Value objects for each save element.
# Populated during runtime by SaveData.register calls.
# @type [Array<Value>]
@values = []
#=============================================================================
# An error raised if an invalid save value is being saved or loaded.
#=============================================================================
class InvalidValueError < RuntimeError; end
#=============================================================================
# Represents a single value in save data.
# New values are added using {SaveData.register}.
#=============================================================================
class Value
# @return [Symbol] the value id
attr_reader :id
@@ -20,6 +26,7 @@ module SaveData
@id = id
@loaded = false
@load_in_bootup = false
@reset_on_new_game = false
instance_eval(&block)
raise "No save_value defined for save value #{id.inspect}" if @save_proc.nil?
raise "No load_value defined for save value #{id.inspect}" if @load_proc.nil?
@@ -70,11 +77,24 @@ module SaveData
return @load_in_bootup
end
def reset_on_new_game
@reset_on_new_game = true
end
def reset_on_new_game?
return @reset_on_new_game
end
# @return [Boolean] whether the value has been loaded
def loaded?
return @loaded
end
# Marks value as unloaded.
def mark_as_unloaded
@loaded = false
end
# Uses the {#from_old_format} proc to select the correct data from
# +old_format+ and return it.
# Returns nil if the proc is undefined.
@@ -85,6 +105,8 @@ module SaveData
return @old_format_get_proc.call(old_format)
end
#---------------------------------------------------------------------------
private
# Raises an {InvalidValueError} if the given value is invalid.
@@ -109,21 +131,21 @@ module SaveData
# Requires a block with the loaded value as its parameter.
# @see SaveData.register
def load_value(&block)
raise ArgumentError, 'No block given to load_value' unless block_given?
raise ArgumentError, "No block given to load_value" unless block_given?
@load_proc = block
end
# Defines what is saved into save data. Requires a block.
# @see SaveData.register
def save_value(&block)
raise ArgumentError, 'No block given to save_value' unless block_given?
raise ArgumentError, "No block given to save_value" unless block_given?
@save_proc = block
end
# If present, defines what the value is set to at the start of a new game.
# @see SaveData.register
def new_game_value(&block)
raise ArgumentError, 'No block given to new_game_value' unless block_given?
raise ArgumentError, "No block given to new_game_value" unless block_given?
@new_game_value_proc = block
end
@@ -137,14 +159,15 @@ module SaveData
# save format. Requires a block with the old format array as its parameter.
# @see SaveData.register
def from_old_format(&block)
raise ArgumentError, 'No block given to from_old_format' unless block_given?
raise ArgumentError, "No block given to from_old_format" unless block_given?
@old_format_get_proc = block
end
# @!endgroup
end
#=============================================================================
#---------------------------------------------------------------------------
# Registers a {Value} to be saved into save data.
# Takes a block which defines the value's saving ({Value#save_value})
# and loading ({Value#load_value}) procedures.
@@ -166,7 +189,6 @@ module SaveData
# save_value { $foo }
# load_value { |value| $foo = value }
# new_game_value { Foo.new }
# from_old_format { |old_format| old_format[16] if old_format[16].is_a?(Foo) }
# end
# @example Registering a value to be loaded on bootup
# SaveData.register(:bar) do
@@ -176,15 +198,20 @@ module SaveData
# new_game_value { Bar.new }
# end
# @param id [Symbol] value id
# @yieldself [Value]
# @yield the block of code to be saved as a Value
def self.register(id, &block)
validate id => Symbol
unless block_given?
raise ArgumentError, 'No block given to SaveData.register'
raise ArgumentError, "No block given to SaveData.register"
end
@values << Value.new(id, &block)
end
def self.unregister(id)
validate id => Symbol
@values.delete_if { |value| value.id == id }
end
# @param save_data [Hash] save data to validate
# @return [Boolean] whether the given save data is valid
def self.valid?(save_data)
@@ -221,13 +248,20 @@ module SaveData
load_values(save_data) { |value| !value.loaded? }
end
# Marks all values that aren't loaded on bootup as unloaded.
def self.mark_values_as_unloaded
@values.each do |value|
value.mark_as_unloaded if !value.load_in_bootup? || value.reset_on_new_game?
end
end
# Loads each value from the given save data that has
# been set to be loaded during bootup. Done when a save file exists.
# @param save_data [Hash] save data to load
# @raise [InvalidValueError] if an invalid value is being loaded
def self.load_bootup_values(save_data)
def self.load_bootup_values(save_data, reload = false)
validate save_data => Hash
load_values(save_data) { |value| !value.loaded? && value.load_in_bootup? }
load_values(save_data) { |value| (reload || !value.loaded?) && value.load_in_bootup? }
end
# Goes through each value with {Value#load_in_bootup} enabled and loads their
@@ -243,7 +277,7 @@ module SaveData
# new game.
def self.load_new_game_values
@values.each do |value|
value.load_new_game_value if value.has_new_game_proc? && !value.loaded?
value.load_new_game_value if value.has_new_game_proc? && (!value.loaded? || value.reset_on_new_game?)
end
end

View File

@@ -1,3 +1,6 @@
#===============================================================================
#
#===============================================================================
module SaveData
# Contains Conversion objects for each defined conversion:
# {
@@ -15,12 +18,13 @@ module SaveData
# Populated during runtime by SaveData.register_conversion calls.
@conversions = {
essentials: {},
game: {}
game: {}
}
#=============================================================================
# Represents a conversion made to save data.
# New conversions are added using {SaveData.register_conversion}.
#=============================================================================
class Conversion
# @return [Symbol] conversion ID
attr_reader :id
@@ -71,6 +75,8 @@ module SaveData
@value_procs[key].call(object) if @value_procs[key].is_a?(Proc)
end
#---------------------------------------------------------------------------
private
# @!group Configuration
@@ -112,7 +118,7 @@ module SaveData
# @see SaveData.register_conversion
def to_value(value_id, &block)
validate value_id => Symbol
raise ArgumentError, 'No block given to to_value' unless block_given?
raise ArgumentError, "No block given to to_value" unless block_given?
if @value_procs[value_id].is_a?(Proc)
raise "Multiple to_value definitions in conversion #{@id} for #{value_id}"
end
@@ -122,7 +128,7 @@ module SaveData
# Defines a conversion to the entire save data.
# @see SaveData.register_conversion
def to_all(&block)
raise ArgumentError, 'No block given to to_all' unless block_given?
raise ArgumentError, "No block given to to_all" unless block_given?
if @all_proc.is_a?(Proc)
raise "Multiple to_all definitions in conversion #{@id}"
end
@@ -132,7 +138,8 @@ module SaveData
# @!endgroup
end
#=============================================================================
#---------------------------------------------------------------------------
# Registers a {Conversion} to occur for save data that meets the given criteria.
# Two types of criteria can be defined: {Conversion#essentials_version} and
# {Conversion#game_version}. The conversion is automatically run on save data
@@ -152,11 +159,11 @@ module SaveData
# save_data[:new_value] = Foo.new
# end
# end
# @yield self [Conversion]
# @yield the block of code to be saved as a Conversion
def self.register_conversion(id, &block)
validate id => Symbol
unless block_given?
raise ArgumentError, 'No block given to SaveData.register_conversion'
raise ArgumentError, "No block given to SaveData.register_conversion"
end
conversion = Conversion.new(id, &block)
@conversions[conversion.trigger_type][conversion.version] ||= []
@@ -168,8 +175,8 @@ module SaveData
def self.get_conversions(save_data)
conversions_to_run = []
versions = {
essentials: save_data[:essentials_version] || '18.1',
game: save_data[:game_version] || '0.0.0'
essentials: save_data[:essentials_version] || "18.1",
game: save_data[:game_version] || "0.0.0"
}
[:essentials, :game].each do |trigger_type|
# Ensure the versions are sorted from lowest to highest
@@ -194,14 +201,15 @@ module SaveData
validate save_data => Hash
conversions_to_run = self.get_conversions(save_data)
return false if conversions_to_run.none?
File.open(SaveData::FILE_PATH + '.bak', 'wb') { |f| Marshal.dump(save_data, f) }
echoln "Running #{conversions_to_run.length} conversions..."
filepath = SaveData::DIRECTORY + SaveData.filename_from_index(save_data[:stats].save_filename_number || 0)
File.open(filepath + ".bak", "wb") { |f| Marshal.dump(save_data, f) }
Console.echo_h1(_INTL("Converting save file"))
conversions_to_run.each do |conversion|
echo "#{conversion.title}..."
Console.echo_li("#{conversion.title}...")
conversion.run(save_data)
echoln ' done.'
Console.echo_done(true)
end
echoln '' if conversions_to_run.length > 0
Console.echoln_li_done(_INTL("Successfully applied {1} save file conversion(s)", conversions_to_run.length))
save_data[:essentials_version] = Essentials::VERSION
save_data[:game_version] = Settings::GAME_VERSION
return true

View File

@@ -1,41 +1,27 @@
#===============================================================================
# Contains the save values defined in Essentials by default.
#===============================================================================
SaveData.register(:player) do
ensure_class :Player
save_value { $Trainer }
load_value { |value| $Trainer = value }
new_game_value {
trainer_type = nil # Get the first defined trainer type as a placeholder
GameData::TrainerType.each { |t| trainer_type = t.id; break }
Player.new("Unnamed", trainer_type)
}
from_old_format { |old_format| old_format[0] }
end
SaveData.register(:frame_count) do
ensure_class :Integer
save_value { Graphics.frame_count }
load_value { |value| Graphics.frame_count = value }
new_game_value { 0 }
from_old_format { |old_format| old_format[1] }
save_value { $player }
load_value { |value| $player = value }
new_game_value { Player.new("Unnamed", GameData::TrainerType.keys.first) }
end
SaveData.register(:game_system) do
load_in_bootup
ensure_class :Game_System
save_value { $game_system }
load_value { |value| $game_system = value }
new_game_value { Game_System.new }
from_old_format { |old_format| old_format[2] }
end
SaveData.register(:pokemon_system) do
load_in_bootup
load_in_bootup # Because this contains values for the Options screen
ensure_class :PokemonSystem
save_value { $PokemonSystem }
load_value { |value| $PokemonSystem = value }
new_game_value { PokemonSystem.new }
from_old_format { |old_format| old_format[3] }
end
SaveData.register(:switches) do
@@ -43,7 +29,6 @@ SaveData.register(:switches) do
save_value { $game_switches }
load_value { |value| $game_switches = value }
new_game_value { Game_Switches.new }
from_old_format { |old_format| old_format[5] }
end
SaveData.register(:variables) do
@@ -51,7 +36,6 @@ SaveData.register(:variables) do
save_value { $game_variables }
load_value { |value| $game_variables = value }
new_game_value { Game_Variables.new }
from_old_format { |old_format| old_format[6] }
end
SaveData.register(:self_switches) do
@@ -59,7 +43,6 @@ SaveData.register(:self_switches) do
save_value { $game_self_switches }
load_value { |value| $game_self_switches = value }
new_game_value { Game_SelfSwitches.new }
from_old_format { |old_format| old_format[7] }
end
SaveData.register(:game_screen) do
@@ -67,14 +50,12 @@ SaveData.register(:game_screen) do
save_value { $game_screen }
load_value { |value| $game_screen = value }
new_game_value { Game_Screen.new }
from_old_format { |old_format| old_format[8] }
end
SaveData.register(:map_factory) do
ensure_class :PokemonMapFactory
save_value { $MapFactory }
load_value { |value| $MapFactory = value }
from_old_format { |old_format| old_format[9] }
save_value { $map_factory }
load_value { |value| $map_factory = value }
end
SaveData.register(:game_player) do
@@ -82,7 +63,6 @@ SaveData.register(:game_player) do
save_value { $game_player }
load_value { |value| $game_player = value }
new_game_value { Game_Player.new }
from_old_format { |old_format| old_format[10] }
end
SaveData.register(:global_metadata) do
@@ -90,7 +70,6 @@ SaveData.register(:global_metadata) do
save_value { $PokemonGlobal }
load_value { |value| $PokemonGlobal = value }
new_game_value { PokemonGlobalMetadata.new }
from_old_format { |old_format| old_format[11] }
end
SaveData.register(:map_metadata) do
@@ -98,15 +77,13 @@ SaveData.register(:map_metadata) do
save_value { $PokemonMap }
load_value { |value| $PokemonMap = value }
new_game_value { PokemonMapMetadata.new }
from_old_format { |old_format| old_format[12] }
end
SaveData.register(:bag) do
ensure_class :PokemonBag
save_value { $PokemonBag }
load_value { |value| $PokemonBag = value }
save_value { $bag }
load_value { |value| $bag = value }
new_game_value { PokemonBag.new }
from_old_format { |old_format| old_format[13] }
end
SaveData.register(:storage_system) do
@@ -114,22 +91,25 @@ SaveData.register(:storage_system) do
save_value { $PokemonStorage }
load_value { |value| $PokemonStorage = value }
new_game_value { PokemonStorage.new }
from_old_format { |old_format| old_format[14] }
end
SaveData.register(:essentials_version) do
load_in_bootup
ensure_class :String
save_value { Essentials::VERSION }
load_value { |value| $SaveVersion = value }
load_value { |value| $save_engine_version = value }
new_game_value { Essentials::VERSION }
from_old_format { |old_format| old_format[15] }
end
SaveData.register(:game_version) do
load_in_bootup
ensure_class :String
save_value { Settings::GAME_VERSION }
load_value { |value| $game_version = value }
load_value { |value| $save_game_version = value }
new_game_value { Settings::GAME_VERSION }
end
SaveData.register(:stats) do
ensure_class :GameStats
save_value { $stats }
load_value { |value| $stats = value }
new_game_value { GameStats.new }
end

View File

@@ -1,242 +1,134 @@
# Contains conversions defined in Essentials by default.
#===============================================================================
# Conversions required to support backwards compatibility with old save files
# (within reason).
#===============================================================================
SaveData.register_conversion(:v19_define_versions) do
essentials_version 19
display_title 'Adding game version and Essentials version to save data'
to_all do |save_data|
unless save_data.has_key?(:essentials_version)
save_data[:essentials_version] = Essentials::VERSION
end
unless save_data.has_key?(:game_version)
save_data[:game_version] = Settings::GAME_VERSION
end
end
end
SaveData.register_conversion(:v19_convert_PokemonSystem) do
essentials_version 19
display_title 'Updating PokemonSystem class'
to_all do |save_data|
new_system = PokemonSystem.new
new_system.textspeed = save_data[:pokemon_system].textspeed || new_system.textspeed
new_system.battlescene = save_data[:pokemon_system].battlescene || new_system.battlescene
new_system.battlestyle = save_data[:pokemon_system].battlestyle || new_system.battlestyle
new_system.frame = save_data[:pokemon_system].frame || new_system.frame
new_system.textskin = save_data[:pokemon_system].textskin || new_system.textskin
new_system.screensize = save_data[:pokemon_system].screensize || new_system.screensize
new_system.language = save_data[:pokemon_system].language || new_system.language
new_system.runstyle = save_data[:pokemon_system].runstyle || new_system.runstyle
new_system.bgmvolume = save_data[:pokemon_system].bgmvolume || new_system.bgmvolume
new_system.sevolume = save_data[:pokemon_system].sevolume || new_system.sevolume
new_system.textinput = save_data[:pokemon_system].textinput || new_system.textinput
save_data[:pokemon_system] = new_system
end
end
SaveData.register_conversion(:v19_convert_player) do
essentials_version 19
display_title 'Converting player trainer class'
to_all do |save_data|
next if save_data[:player].is_a?(Player)
# Conversion of the party is handled in PokeBattle_Trainer.convert
save_data[:player] = PokeBattle_Trainer.convert(save_data[:player])
end
end
SaveData.register_conversion(:v19_move_global_data_to_player) do
essentials_version 19
display_title 'Moving some global metadata data to player'
to_all do |save_data|
global = save_data[:global_metadata]
player = save_data[:player]
player.character_ID = global.playerID
global.playerID = nil
global.pokedexUnlocked.each_with_index do |value, i|
if value
player.pokedex.unlock(i)
else
player.pokedex.lock(i)
SaveData.register_conversion(:v21_replace_phone_data) do
essentials_version 21
display_title "Updating Phone data format"
to_value :global_metadata do |global|
if !global.phone
global.instance_eval do
@phone = Phone.new
@phoneTime = nil # Don't bother using this
if @phoneNumbers
@phoneNumbers.each do |contact|
if contact.length > 4
# Trainer
@phone.add(contact[6], contact[7], contact[1], contact[2], contact[5], 0)
new_contact = @phone.get(contact[1], contact[2], 0)
new_contact.visible = contact[0]
new_contact.rematch_flag = [contact[4] - 1, 0].max
else
# Non-trainer
@phone.add(contact[3], contact[2], contact[1])
end
end
@phoneNumbers = nil
end
end
end
player.coins = global.coins
global.coins = nil
player.soot = global.sootsack
global.sootsack = nil
player.has_running_shoes = global.runningShoes
global.runningShoes = nil
player.seen_storage_creator = global.seenStorageCreator
global.seenStorageCreator = nil
player.has_snag_machine = global.snagMachine
global.snagMachine = nil
player.seen_purify_chamber = global.seenPurifyChamber
global.seenPurifyChamber = nil
end
end
SaveData.register_conversion(:v19_convert_global_metadata) do
essentials_version 19
display_title 'Adding encounter version variable to global metadata'
to_value :global_metadata do |global|
global.bridge ||= 0
global.encounter_version ||= 0
if global.pcItemStorage
global.pcItemStorage.items.each_with_index do |slot, i|
item_data = GameData::Item.try_get(slot[0])
if item_data
slot[0] = item_data.id
#===============================================================================
SaveData.register_conversion(:v21_replace_flute_booleans) do
essentials_version 21
display_title "Updating Black/White Flute variables"
to_value :map_metadata do |metadata|
metadata.instance_eval do
if !@blackFluteUsed.nil?
if Settings::FLUTES_CHANGE_WILD_ENCOUNTER_LEVELS
@higher_level_wild_pokemon = @blackFluteUsed
else
global.pcItemStorage.items[i] = nil
@lower_encounter_rate = @blackFluteUsed
end
@blackFluteUsed = nil
end
global.pcItemStorage.items.compact!
end
if global.mailbox
global.mailbox.each_with_index do |mail, i|
global.mailbox[i] = PokemonMail.convert(mail) if mail
end
end
global.phoneNumbers.each do |contact|
contact[1] = GameData::TrainerType.get(contact[1]).id if contact && contact.length == 8
end
if global.partner
global.partner[0] = GameData::TrainerType.get(global.partner[0]).id
global.partner[3].each_with_index do |pkmn, i|
global.partner[3][i] = PokeBattle_Pokemon.convert(pkmn) if pkmn
end
end
if global.daycare
global.daycare.each do |slot|
slot[0] = PokeBattle_Pokemon.convert(slot[0]) if slot && slot[0]
end
end
if global.roamPokemon
global.roamPokemon.each_with_index do |pkmn, i|
global.roamPokemon[i] = PokeBattle_Pokemon.convert(pkmn) if pkmn && pkmn != true
end
end
global.purifyChamber.sets.each do |set|
set.shadow = PokeBattle_Pokemon.convert(set.shadow) if set.shadow
set.list.each_with_index do |pkmn, i|
set.list[i] = PokeBattle_Pokemon.convert(pkmn) if pkmn
end
end
if global.hallOfFame
global.hallOfFame.each do |team|
next if !team
team.each_with_index do |pkmn, i|
team[i] = PokeBattle_Pokemon.convert(pkmn) if pkmn
if !@whiteFluteUsed.nil?
if Settings::FLUTES_CHANGE_WILD_ENCOUNTER_LEVELS
@lower_level_wild_pokemon = @whiteFluteUsed
else
@higher_encounter_rate = @whiteFluteUsed
end
end
end
if global.triads
global.triads.items.each do |card|
card[0] = GameData::Species.get(card[0]).id if card && card[0] && card[0] != 0
@whiteFluteUsed = nil
end
end
end
end
SaveData.register_conversion(:v19_1_fix_phone_contacts) do
essentials_version 19.1
display_title 'Fixing phone contacts data'
to_value :global_metadata do |global|
global.phoneNumbers.each do |contact|
contact[1] = GameData::TrainerType.get(contact[1]).id if contact && contact.length == 8
#===============================================================================
SaveData.register_conversion(:v21_add_bump_stat) do
essentials_version 21
display_title "Adding a bump stat"
to_value :stats do |stats|
stats.instance_eval do
@bump_count = 0 if !@bump_count
end
end
end
SaveData.register_conversion(:v19_convert_bag) do
essentials_version 19
display_title 'Converting item IDs in Bag'
#===============================================================================
SaveData.register_conversion(:v22_add_adventure_magic_number) do
essentials_version 22
display_title "Adding adventure ID"
to_value :game_system do |game_system|
game_system.instance_eval do
@adventure_magic_number ||= rand(2**32)
end
end
end
#===============================================================================
SaveData.register_conversion(:v22_add_new_stats) do
essentials_version 22
display_title "Adding some more stats"
to_value :stats do |stats|
stats.instance_eval do
@wild_battles_fled = 0 if !@wild_battles_fled
@pokemon_release_count = 0 if !@pokemon_release_count
@primal_reversion_count = 0 if !@primal_reversion_count
end
end
end
#===============================================================================
SaveData.register_conversion(:v22_convert_bag_object) do
essentials_version 22
display_title "Converting Bag's pockets"
to_value :bag do |bag|
bag.instance_eval do
for pocket in self.pockets
pocket.each_with_index do |item, i|
next if !item || !item[0] || item[0] == 0
item_data = GameData::Item.try_get(item[0])
if item_data
item[0] = item_data.id
else
pocket[i] = nil
all_pockets = GameData::BagPocket.all_pockets
if @pockets.is_a?(Array)
new_pockets = {}
all_pockets.each { |pckt| new_pockets[pckt] = [] }
@pockets.each_with_index do |value, i|
next if i == 0
value.each do |item|
pckt = GameData::Item.get(item[0]).bag_pocket
new_pockets[pckt].push(item)
end
end
pocket.compact!
@pockets = new_pockets
end
self.registeredIndex # Just to ensure this data exists
self.registeredItems.each_with_index do |item, i|
next if !item
if item == 0
self.registeredItems[i] = nil
else
item_data = GameData::Item.try_get(item)
if item_data
self.registeredItems[i] = item_data.id
else
self.registeredItems[i] = nil
end
end
if @last_viewed_pocket.is_a?(Integer)
@last_viewed_pocket = all_pockets[@last_viewed_pocket - 1] || all_pockets.first
end
self.registeredItems.compact!
end # bag.instance_eval
end # to_value
end
SaveData.register_conversion(:v19_convert_game_variables) do
essentials_version 19
display_title 'Converting classes of things in Game Variables'
to_all do |save_data|
variables = save_data[:variables]
for i in 0..5000
value = variables[i]
next if value.nil?
if value.is_a?(Array)
value.each_with_index do |value2, j|
if value2.is_a?(PokeBattle_Pokemon)
value[j] = PokeBattle_Pokemon.convert(value2)
end
if @last_pocket_selections.is_a?(Array)
new_sels = {}
all_pockets.each { |pckt| new_sels[pckt] = 0 }
@last_pocket_selections.each_with_index do |value, i|
next if i == 0
pckt = all_pockets[i - 1]
new_sels[pckt] = value if pckt && value <= @pockets[pckt].length - 1
end
elsif value.is_a?(PokeBattle_Pokemon)
variables[i] = PokeBattle_Pokemon.convert(value)
elsif value.is_a?(PokemonBag)
SaveData.run_single_conversions(value, :bag, save_data)
@last_pocket_selections = new_sels
end
end
end
end
SaveData.register_conversion(:v19_convert_storage) do
essentials_version 19
display_title 'Converting classes of Pokémon in storage'
to_value :storage_system do |storage|
storage.instance_eval do
for box in 0...self.maxBoxes
for i in 0...self.maxPokemon(box)
self[box, i] = PokeBattle_Pokemon.convert(self[box, i]) if self[box, i]
end
end
self.unlockedWallpapers # Just to ensure this data exists
end # storage.instance_eval
end # to_value
end
SaveData.register_conversion(:v19_convert_game_player) do
essentials_version 19
display_title 'Converting game player character'
to_value :game_player do |game_player|
game_player.width = 1
game_player.height = 1
game_player.sprite_size = [Game_Map::TILE_WIDTH, Game_Map::TILE_HEIGHT]
game_player.pattern_surf ||= 0
game_player.lock_pattern ||= false
game_player.move_speed = game_player.move_speed
end
end
SaveData.register_conversion(:v19_convert_game_screen) do
essentials_version 19
display_title 'Converting game screen'
to_value :game_screen do |game_screen|
game_screen.weather(game_screen.weather_type, game_screen.weather_max, 0)
end
end

View File

@@ -1,52 +1,53 @@
#===============================================================================
# The Game module contains methods for saving and loading the game.
#===============================================================================
module Game
module_function
# Initializes various global variables and loads the game data.
def self.initialize
$PokemonTemp = PokemonTemp.new
def initialize
$game_temp = Game_Temp.new
$game_system = Game_System.new
$data_animations = load_data('Data/Animations.rxdata')
$data_tilesets = load_data('Data/Tilesets.rxdata')
$data_common_events = load_data('Data/CommonEvents.rxdata')
$data_system = load_data('Data/System.rxdata')
$data_animations = load_data("Data/Animations.rxdata")
$data_tilesets = load_data("Data/Tilesets.rxdata")
$data_common_events = load_data("Data/CommonEvents.rxdata")
$data_system = load_data("Data/System.rxdata")
pbLoadBattleAnimations
GameData.load_all
map_file = format('Data/Map%03d.rxdata', $data_system.start_map_id)
map_file = sprintf("Data/Map%03d.rxdata", $data_system.start_map_id)
if $data_system.start_map_id == 0 || !pbRgssExists?(map_file)
raise _INTL('No starting position was set in the map editor.')
raise _INTL("No starting position was set in the map editor.")
end
end
# Loads bootup data from save file (if it exists) or creates bootup data (if
# it doesn't).
def self.set_up_system
SaveData.move_old_windows_save if System.platform[/Windows/]
save_data = (SaveData.exists?) ? SaveData.read_from_file(SaveData::FILE_PATH) : {}
if save_data.empty?
SaveData.initialize_bootup_values
else
SaveData.load_bootup_values(save_data)
end
# Set resize factor
def set_up_system
SaveData.initialize_bootup_values
pbSetResizeFactor([$PokemonSystem.screensize, 4].min)
# Set language (and choose language if there is no save file)
if Settings::LANGUAGES.length >= 2
$PokemonSystem.language = pbChooseLanguage if save_data.empty?
pbLoadMessages('Data/' + Settings::LANGUAGES[$PokemonSystem.language][1])
if !Settings::LANGUAGES.empty?
$PokemonSystem.language = pbChooseLanguage if !SaveData.exists? && Settings::LANGUAGES.length >= 2
MessageTypes.load_message_files(Settings::LANGUAGES[$PokemonSystem.language][1])
end
end
# Called when starting a new game. Initializes global variables
# and transfers the player into the map scene.
def self.start_new
if $game_map && $game_map.events
def start_new
# Essentials 21 renamed the global variable $Trainer
# It's still used everywhere in events, global events so this makes things simpler
if $game_map&.events
$game_map.events.each_value { |event| event.clear_starting }
end
$game_temp.common_event_id = 0 if $game_temp
$PokemonTemp.begunNewGame = true
pbMapInterpreter&.clear
pbMapInterpreter&.setup(nil, 0, 0)
$scene = Scene_Map.new
SaveData.load_new_game_values
$MapFactory = PokemonMapFactory.new($data_system.start_map_id)
$game_temp.last_uptime_refreshed_play_time = System.uptime
$stats.play_sessions += 1
$map_factory = PokemonMapFactory.new($data_system.start_map_id)
$game_player.moveto($data_system.start_x, $data_system.start_y)
$game_player.refresh
$PokemonEncounters = PokemonEncounters.new
@@ -58,10 +59,17 @@ module Game
# Loads the game from the given save data and starts the map scene.
# @param save_data [Hash] hash containing the save data
# @raise [SaveData::InvalidValueError] if an invalid value is being loaded
def self.load(save_data)
def load(save_data)
# Essentials 21 renamed the global variable $Trainer
# It's still used everywhere in events, global events so this makes things simpler
$Trainer = $player
validate save_data => Hash
SaveData.load_all_values(save_data)
self.load_map
$game_temp.last_uptime_refreshed_play_time = System.uptime
$stats.play_sessions += 1
load_map
pbAutoplayOnSave
$game_map.update
$PokemonMap.updateMap
@@ -69,32 +77,30 @@ module Game
end
# Loads and validates the map. Called when loading a saved game.
def self.load_map
$game_map = $MapFactory.map
def load_map
$game_map = $map_factory.map
magic_number_matches = ($game_system.magic_number == $data_system.magic_number)
if !magic_number_matches || $PokemonGlobal.safesave
if pbMapInterpreterRunning?
pbMapInterpreter.setup(nil, 0)
end
pbMapInterpreter.setup(nil, 0) if pbMapInterpreterRunning?
begin
$MapFactory.setup($game_map.map_id)
$map_factory.setup($game_map.map_id)
rescue Errno::ENOENT
if $DEBUG
pbMessage(_INTL('Map {1} was not found.', $game_map.map_id))
pbMessage(_INTL("Map {1} was not found.", $game_map.map_id))
map = pbWarpToMap
exit unless map
$MapFactory.setup(map[0])
$map_factory.setup(map[0])
$game_player.moveto(map[1], map[2])
else
raise _INTL('The map was not found. The game cannot continue.')
raise _INTL("The map was not found. The game cannot continue.")
end
end
$game_player.center($game_player.x, $game_player.y)
else
$MapFactory.setMapChanged($game_map.map_id)
$map_factory.setMapChanged($game_map.map_id)
end
if $game_map.events.nil?
raise _INTL('The map is corrupt. The game cannot continue.')
raise _INTL("The map is corrupt. The game cannot continue.")
end
$PokemonEncounters = PokemonEncounters.new
$PokemonEncounters.setup($game_map.map_id)
@@ -102,17 +108,21 @@ module Game
end
# Saves the game. Returns whether the operation was successful.
# @param save_file [String] the save file path
# @param index [Integer] the number to put in the save file's name Game#.rzdata
# @param directory [String] the folder to put the save file in
# @param safe [Boolean] whether $PokemonGlobal.safesave should be set to true
# @return [Boolean] whether the operation was successful
# @raise [SaveData::InvalidValueError] if an invalid value is being saved
def self.save(save_file = SaveData::FILE_PATH, safe: false)
validate save_file => String, safe => [TrueClass, FalseClass]
def save(index, directory = SaveData::DIRECTORY, safe: false)
validate index => Integer, directory => String, safe => [TrueClass, FalseClass]
filename = SaveData.filename_from_index(index)
$PokemonGlobal.safesave = safe
$game_system.save_count += 1
$game_system.magic_number = $data_system.magic_number
$stats.set_time_last_saved
$stats.save_filename_number = index
begin
SaveData.save_to_file(save_file)
SaveData.save_to_file(directory + filename)
Graphics.frame_reset
rescue IOError, SystemCallError
$game_system.save_count -= 1

View File

@@ -1,45 +1,52 @@
#===============================================================================
# ** Modified Scene_Map class for Pokémon.
#-------------------------------------------------------------------------------
#
# Modified Scene_Map class for Pokémon.
#===============================================================================
class Scene_Map
attr_reader :spritesetGlobal
attr_reader :map_renderer
def spriteset
for i in @spritesets.values
return i if i.map==$game_map
def spriteset(map_id = -1)
return @spritesets[map_id] if map_id > 0 && @spritesets[map_id]
@spritesets.each_value do |i|
return i if i.map == $game_map
end
return @spritesets.values[0]
end
def createSpritesets
@spritesetGlobal = Spriteset_Global.new
@map_renderer = TilemapRenderer.new(Spriteset_Map.viewport) if !@map_renderer || @map_renderer.disposed?
@spritesetGlobal = Spriteset_Global.new if !@spritesetGlobal
@spritesets = {}
for map in $MapFactory.maps
$map_factory.maps.each do |map|
@spritesets[map.map_id] = Spriteset_Map.new(map)
end
$MapFactory.setSceneStarted(self)
updateSpritesets
$map_factory.setSceneStarted(self)
updateSpritesets(true)
end
def createSingleSpriteset(map)
temp = $scene.spriteset.getAnimations
@spritesets[map] = Spriteset_Map.new($MapFactory.maps[map])
@spritesets[map] = Spriteset_Map.new($map_factory.maps[map])
$scene.spriteset.restoreAnimations(temp)
$MapFactory.setSceneStarted(self)
updateSpritesets
$map_factory.setSceneStarted(self)
updateSpritesets(true)
end
def disposeSpritesets
return if !@spritesets
for i in @spritesets.keys
@spritesets.each_key do |i|
next if !@spritesets[i]
@spritesets[i].dispose
@spritesets[i] = nil
end
@spritesets.clear
@spritesets = {}
end
def dispose
disposeSpritesets
@map_renderer.dispose
@map_renderer = nil
@spritesetGlobal.dispose
@spritesetGlobal = nil
end
@@ -50,26 +57,24 @@ class Scene_Map
return if !playingBGM && !playingBGS
map = load_data(sprintf("Data/Map%03d.rxdata", mapid))
if playingBGM && map.autoplay_bgm
if (PBDayNight.isNight? rescue false)
pbBGMFade(0.8) if playingBGM.name!=map.bgm.name && playingBGM.name!=map.bgm.name+"_n"
else
pbBGMFade(0.8) if playingBGM.name!=map.bgm.name
end
test_filename = map.bgm.name
test_filename += "_n" if PBDayNight.isNight? && FileTest.audio_exist?("Audio/BGM/" + test_filename + "_n")
pbBGMFade(0.8) if playingBGM.name != test_filename
end
if playingBGS && map.autoplay_bgs
pbBGMFade(0.8) if playingBGS.name!=map.bgs.name
if playingBGS && map.autoplay_bgs && playingBGS.name != map.bgs.name
pbBGMFade(0.8)
end
Graphics.frame_reset
end
def transfer_player(cancelVehicles=true)
def transfer_player(cancel_swimming = true)
$game_temp.player_transferring = false
pbCancelVehicles($game_temp.player_new_map_id) if cancelVehicles
pbCancelVehicles($game_temp.player_new_map_id, cancel_swimming)
autofade($game_temp.player_new_map_id)
pbBridgeOff
@spritesetGlobal.playersprite.clearShadows
if $game_map.map_id!=$game_temp.player_new_map_id
$MapFactory.setup($game_temp.player_new_map_id)
if $game_map.map_id != $game_temp.player_new_map_id
$map_factory.setup($game_temp.player_new_map_id)
end
$game_player.moveto($game_temp.player_new_x, $game_temp.player_new_y)
case $game_temp.player_new_direction
@@ -79,13 +84,14 @@ class Scene_Map
when 8 then $game_player.turn_up
end
$game_player.straighten
$game_temp.followers.map_transfer_followers
$game_map.update
disposeSpritesets
RPG::Cache.clear
createSpritesets
if $game_temp.transition_processing
$game_temp.transition_processing = false
Graphics.transition(20)
Graphics.transition
end
$game_map.autoplay
Graphics.frame_reset
@@ -97,9 +103,7 @@ class Scene_Map
$game_temp.in_menu = true
$game_player.straighten
$game_map.update
sscene = PokemonPauseMenu_Scene.new
sscreen = PokemonPauseMenu.new(sscene)
sscreen.pbStartPokemonMenu
UI::PauseMenu.new.main
$game_temp.in_menu = false
end
@@ -111,107 +115,119 @@ class Scene_Map
end
def miniupdate
$PokemonTemp.miniupdate = true
$game_temp.in_mini_update = true
loop do
updateMaps
$game_player.update
updateMaps
$game_system.update
$game_screen.update
break unless $game_temp.player_transferring
transfer_player
break if !$game_temp.player_transferring
transfer_player(false)
break if $game_temp.transition_processing
end
updateSpritesets
$PokemonTemp.miniupdate = false
$game_temp.in_mini_update = false
end
def updateMaps
for map in $MapFactory.maps
$map_factory.maps.each do |map|
map.update
end
$MapFactory.updateMaps(self)
$map_factory.updateMaps(self)
end
def updateSpritesets
def updateSpritesets(refresh = false)
@spritesets = {} if !@spritesets
$map_factory.maps.each do |map|
@spritesets[map.map_id] = Spriteset_Map.new(map) if !@spritesets[map.map_id]
end
keys = @spritesets.keys.clone
for i in keys
if !$MapFactory.hasMap?(i)
@spritesets[i].dispose if @spritesets[i]
keys.each do |i|
if $map_factory.hasMap?(i)
@spritesets[i].update
else
@spritesets[i]&.dispose
@spritesets[i] = nil
@spritesets.delete(i)
else
@spritesets[i].update
end
end
@spritesetGlobal.update
for map in $MapFactory.maps
@spritesets[map.map_id] = Spriteset_Map.new(map) if !@spritesets[map.map_id]
end
Events.onMapUpdate.trigger(self)
pbDayNightTint(@map_renderer)
@map_renderer.refresh if refresh
@map_renderer.update
EventHandlers.trigger(:on_frame_update)
end
def update
loop do
updateMaps
pbMapInterpreter.update
$game_player.update
updateMaps
$game_system.update
$game_screen.update
break unless $game_temp.player_transferring
transfer_player
break if !$game_temp.player_transferring
transfer_player(false)
break if $game_temp.transition_processing
end
updateSpritesets
if $game_temp.to_title
if $game_temp.title_screen_calling
SaveData.mark_values_as_unloaded
$scene = pbCallTitle
return
end
if $game_temp.transition_processing
$game_temp.transition_processing = false
if $game_temp.transition_name == ""
Graphics.transition(20)
Graphics.transition
else
Graphics.transition(40, "Graphics/Transitions/" + $game_temp.transition_name)
end
end
return if $game_temp.message_window_showing
if !pbMapInterpreterRunning?
if !pbMapInterpreterRunning? && !$PokemonGlobal.forced_movement?
if Input.trigger?(Input::USE)
$PokemonTemp.hiddenMoveEventCalling = true
elsif Input.trigger?(Input::BACK)
unless $game_system.menu_disabled || $game_player.moving?
$game_temp.interact_calling = true
elsif Input.trigger?(Input::ACTION)
if !$game_system.menu_disabled && !$game_player.moving?
$game_temp.menu_calling = true
$game_temp.menu_beep = true
end
elsif Input.trigger?(Input::SPECIAL)
unless $game_player.moving?
$PokemonTemp.keyItemCalling = true
end
$game_temp.ready_menu_calling = true if !$game_player.moving?
elsif Input.press?(Input::F9)
$game_temp.debug_calling = true if $DEBUG
end
end
unless $game_player.moving?
if !$game_player.moving?
if $game_temp.menu_calling
call_menu
elsif $game_temp.debug_calling
call_debug
elsif $PokemonTemp.keyItemCalling
$PokemonTemp.keyItemCalling = false
elsif $game_temp.ready_menu_calling
$game_temp.ready_menu_calling = false
$game_player.straighten
pbUseKeyItem
elsif $PokemonTemp.hiddenMoveEventCalling
$PokemonTemp.hiddenMoveEventCalling = false
$game_player.straighten
Events.onAction.trigger(self)
elsif $game_temp.interact_calling
$game_temp.interact_calling = false
triggered = false
# Try to trigger an event the player is standing on, and one in front of
# the player
if !$game_temp.in_mini_update
triggered ||= $game_player.check_event_trigger_here([0])
triggered ||= $game_player.check_event_trigger_there([0, 2]) if !triggered
end
# Try to trigger an interaction with a tile
if !triggered
$game_player.straighten
EventHandlers.trigger(:on_player_interact)
end
end
end
end
def main
createSpritesets
Graphics.transition(20)
Graphics.transition
loop do
Graphics.update
Input.update
@@ -219,9 +235,13 @@ class Scene_Map
break if $scene != self
end
Graphics.freeze
disposeSpritesets
if $game_temp.to_title
Graphics.transition(20)
dispose
if $game_temp.title_screen_calling
pbMapInterpreter.command_end if pbMapInterpreterRunning?
$game_temp.last_uptime_refreshed_play_time = nil
$game_temp.title_screen_calling = false
pbBGMFade(1.0)
Graphics.transition
Graphics.freeze
end
end

View File

@@ -5,11 +5,9 @@
# Game_System class and the Game_Event class.
#===============================================================================
class Interpreter
#-----------------------------------------------------------------------------
# * Object Initialization
# Object Initialization
# depth : nest depth
# main : main flag
#-----------------------------------------------------------------------------
def initialize(depth = 0, main = false)
@depth = depth
@main = main
@@ -22,25 +20,28 @@ class Interpreter
def inspect
str = super.chop
str << format(' @event_id: %d>', @event_id)
str << sprintf(" @event_id: %d>", @event_id)
return str
end
def clear
@map_id = 0 # map ID when starting up
@event_id = 0 # event ID
@event_id = 0
@message_waiting = false # waiting for message to end
@move_route_waiting = false # waiting for move completion
@wait_count = 0 # wait count
@child_interpreter = nil # child interpreter
@branch = {} # branch data
@wait_count = 0
@wait_start = nil
@child_interpreter = nil
@branch = {}
@buttonInput = false
@hidden_choices = []
@renamed_choices = []
end_follower_overrides
end
#-----------------------------------------------------------------------------
# * Event Setup
# Event Setup
# list : list of event commands
# event_id : event ID
#-----------------------------------------------------------------------------
def setup(list, event_id, map_id = nil)
clear
@map_id = map_id || $game_map.map_id
@@ -59,7 +60,7 @@ class Interpreter
return
end
# Check all map events for one that wants to start, and set it up
for event in $game_map.events.values
$game_map.events.each_value do |event|
next if !event.starting
if event.trigger < 3 # Isn't autorun or parallel processing
event.lock
@@ -69,7 +70,7 @@ class Interpreter
return
end
# Check all common events for one that is autorun, and set it up
for common_event in $data_common_events.compact
$data_common_events.compact.each do |common_event|
next if common_event.trigger != 1 || !$game_switches[common_event.switch_id]
setup(common_event.list, 0)
return
@@ -77,11 +78,9 @@ class Interpreter
end
def running?
return @list != nil
return !@list.nil?
end
#-----------------------------------------------------------------------------
# * Frame Update
#-----------------------------------------------------------------------------
def update
@loop_count = 0
loop do
@@ -92,7 +91,7 @@ class Interpreter
end
# If this interpreter's map isn't the current map or connected to it,
# forget this interpreter's event ID
if $game_map.map_id != @map_id && !$MapFactory.areConnected?($game_map.map_id, @map_id)
if $game_map.map_id != @map_id && !$map_factory.areConnected?($game_map.map_id, @map_id)
@event_id = 0
end
# Update child interpreter if one exists
@@ -106,15 +105,21 @@ class Interpreter
# Do nothing if any event or the player is in the middle of a move route
if @move_route_waiting
return if $game_player.move_route_forcing
for event in $game_map.events.values
$game_map.events.each_value do |event|
return if event.move_route_forcing
end
$game_temp.followers.each_follower do |event, follower|
return if event.move_route_forcing
end
@move_route_waiting = false
end
# Do nothing if the player is jumping out of surfing
return if $game_temp.ending_surf
# Do nothing while waiting
if @wait_count > 0
@wait_count -= 1
return
return if System.uptime - @wait_start < @wait_count
@wait_count = 0
@wait_start = nil
end
# Do nothing if the pause menu is going to open
return if $game_temp.menu_calling
@@ -129,70 +134,52 @@ class Interpreter
@index += 1
end
end
#-----------------------------------------------------------------------------
# * Execute script
#-----------------------------------------------------------------------------
def execute_script(script)
begin
result = eval(script)
return result
rescue Exception
e = $!
raise if e.is_a?(SystemExit) || "#{e.class}" == "Reset"
raise if e.is_a?(SystemExit) || e.class.to_s == "Reset"
event = get_self
s = "Backtrace:\r\n"
# Gather text for error message
message = pbGetExceptionMessage(e)
backtrace_text = ""
if e.is_a?(SyntaxError)
script.each_line { |line|
script.each_line do |line|
line.gsub!(/\s+$/, "")
if line[/^\s*\(/]
message += "\r\n***Line '#{line}' shouldn't begin with '('. Try\r\n"
message += "putting the '(' at the end of the previous line instead,\r\n"
message += "or using 'extendtext.exe'."
message += "\r\n***Line '#{line}' shouldn't begin with '('. Try putting the '('\r\n"
message += "at the end of the previous line instead, or using 'extendtext.exe'."
end
if line[/\:\:\s*$/]
message += "\r\n***Line '#{line}' can't end with '::'. Try putting\r\n"
message += "the next word on the same line, e.g. 'PBSpecies:" + ":MEW'"
end
}
else
for bt in e.backtrace[0, 10]
s += bt + "\r\n"
end
s.gsub!(/Section(\d+)/) { $RGSS_SCRIPTS[$1.to_i][1] }
end
message = "Exception: #{e.class}\r\nMessage: " + message + "\r\n"
message += "\r\n***Full script:\r\n#{script}\r\n"
if event && $game_map
map_name = ($game_map.name rescue nil) || "???"
err = "Script error in event #{event.id} (coords #{event.x},#{event.y}), map #{$game_map.map_id} (#{map_name}):\r\n"
err += "#{message}\r\n#{s}"
if e.is_a?(Hangup)
$EVENTHANGUPMSG = err
raise
end
elsif $game_map
map_name = ($game_map.name rescue nil) || "???"
err = "Script error in map #{$game_map.map_id} (#{map_name}):\r\n"
err += "#{message}\r\n#{s}"
if e.is_a?(Hangup)
$EVENTHANGUPMSG = err
raise
end
else
err = "Script error in interpreter:\r\n#{message}\r\n#{s}"
if e.is_a?(Hangup)
$EVENTHANGUPMSG = err
raise
backtrace_text += "\r\n"
backtrace_text += "Backtrace:"
e.backtrace[0, 10].each { |i| backtrace_text += "\r\n#{i}" }
backtrace_text.gsub!(/Section(\d+)/) { $RGSS_SCRIPTS[$1.to_i][1] } rescue nil
backtrace_text += "\r\n"
end
# Assemble error message
err = "Script error in Interpreter\r\n"
if $game_map
map_name = (pbGetBasicMapNameFromId($game_map.map_id) rescue nil) || "???"
if event
err = "Script error in event #{event.id} (coords #{event.x},#{event.y}), map #{$game_map.map_id} (#{map_name})\r\n"
else
err = "Script error in Common Event, map #{$game_map.map_id} (#{map_name})\r\n"
end
end
raise err
err += "Exception: #{e.class}\r\n"
err += "Message: #{message}\r\n\r\n"
err += "***Full script:\r\n#{script}" # \r\n"
err += backtrace_text
# Raise error
raise EventScriptError.new(err)
end
end
#-----------------------------------------------------------------------------
# * Get Character
# parameter : parameter
#-----------------------------------------------------------------------------
def get_character(parameter = 0)
case parameter
when -1 # player
@@ -217,21 +204,18 @@ class Interpreter
def get_event(parameter)
return get_character(parameter)
end
#-----------------------------------------------------------------------------
# * Freezes all events on the map (for use at the beginning of common events)
#-----------------------------------------------------------------------------
# Freezes all events on the map (for use at the beginning of common events)
def pbGlobalLock
$game_map.events.values.each { |event| event.minilock }
$game_map.events.each_value { |event| event.minilock }
end
#-----------------------------------------------------------------------------
# * Unfreezes all events on the map (for use at the end of common events)
#-----------------------------------------------------------------------------
# Unfreezes all events on the map (for use at the end of common events)
def pbGlobalUnlock
$game_map.events.values.each { |event| event.unlock }
$game_map.events.each_value { |event| event.unlock }
end
#-----------------------------------------------------------------------------
# * Gets the next index in the interpreter, ignoring certain commands between messages
#-----------------------------------------------------------------------------
# Gets the next index in the interpreter, ignoring certain commands between messages
def pbNextIndex(index)
return -1 if !@list || @list.length == 0
i = index + 1
@@ -281,12 +265,27 @@ class Interpreter
temp_index += 1
end
end
#-----------------------------------------------------------------------------
# * Various methods to be used in a script event command.
#-----------------------------------------------------------------------------
def follower_move_route(id = nil)
@follower_move_route = true
@follower_move_route_id = id
end
def follower_animation(id = nil)
@follower_animation = true
@follower_animation_id = id
end
def end_follower_overrides
@follower_move_route = false
@follower_move_route_id = nil
@follower_animation = false
@follower_animation_id = nil
end
# Helper function that shows a picture in a script.
def pbShowPicture(number, name, origin, x, y, zoomX = 100, zoomY = 100, opacity = 255, blendType = 0)
number = number + ($game_temp.in_battle ? 50 : 0)
number += ($game_temp.in_battle ? 50 : 0)
$game_screen.pictures[number].show(name, origin, x, y, zoomX, zoomY, opacity, blendType)
end
@@ -295,7 +294,7 @@ class Interpreter
def pbEraseThisEvent
if $game_map.events[@event_id]
$game_map.events[@event_id].erase
$PokemonMap.addErasedEvent(@event_id) if $PokemonMap
$PokemonMap&.addErasedEvent(@event_id)
end
@index += 1
return true
@@ -325,8 +324,8 @@ class Interpreter
mapid = @map_id if mapid < 0
old_value = $game_self_switches[[mapid, eventid, switch_name]]
$game_self_switches[[mapid, eventid, switch_name]] = value
if value != old_value && $MapFactory.hasMap?(mapid)
$MapFactory.getMap(mapid, false).need_refresh = true
if value != old_value && $map_factory.hasMap?(mapid)
$map_factory.getMap(mapid, false).need_refresh = true
end
end
@@ -368,7 +367,7 @@ class Interpreter
end
def pbGetPokemon(id)
return $Trainer.party[pbGet(id)]
return $player.party[pbGet(id)]
end
def pbSetEventTime(*arg)
@@ -377,28 +376,30 @@ class Interpreter
time = time.to_i
pbSetSelfSwitch(@event_id, "A", true)
$PokemonGlobal.eventvars[[@map_id, @event_id]] = time
for otherevt in arg
arg.each do |otherevt|
pbSetSelfSwitch(otherevt, "A", true)
$PokemonGlobal.eventvars[[@map_id, otherevt]] = time
end
end
# Used in boulder events. Allows an event to be pushed.
def pbPushThisEvent
def pbPushThisEvent(strength = false)
event = get_self
old_x = event.x
old_y = event.y
# Apply strict version of passable, which treats tiles that are passable
# only from certain directions as fully impassible
return if !event.can_move_in_direction?($game_player.direction, true)
$stats.strength_push_count += 1
case $game_player.direction
when 2 then event.move_down
when 4 then event.move_left
when 6 then event.move_right
when 8 then event.move_up
end
$PokemonMap.addMovedEvent(@event_id) if $PokemonMap
$PokemonMap&.addMovedEvent(@event_id)
if old_x != event.x || old_y != event.y
pbSEPlay("Strength push") if strength
$game_player.lock
loop do
Graphics.update
@@ -411,7 +412,7 @@ class Interpreter
end
def pbPushThisBoulder
pbPushThisEvent if $PokemonMap.strengthUsed
pbPushThisEvent(true) if $PokemonMap.strengthUsed
return true
end
@@ -426,14 +427,14 @@ class Interpreter
return true if $DEBUG && !GameData::TrainerType.exists?(symbol)
tr_type = GameData::TrainerType.get(symbol).id
pbGlobalLock
pbPlayTrainerIntroME(tr_type)
pbPlayTrainerIntroBGM(tr_type)
return true
end
def pbTrainerEnd
pbGlobalUnlock
event = get_self
event.erase_route if event
event&.erase_route
end
def setPrice(item, buy_price = -1, sell_price = -1)
@@ -441,9 +442,9 @@ class Interpreter
$game_temp.mart_prices[item] = [-1, -1] if !$game_temp.mart_prices[item]
$game_temp.mart_prices[item][0] = buy_price if buy_price > 0
if sell_price >= 0 # 0=can't sell
$game_temp.mart_prices[item][1] = sell_price * 2
else
$game_temp.mart_prices[item][1] = buy_price if buy_price > 0
$game_temp.mart_prices[item][1] = sell_price
elsif buy_price > 0
$game_temp.mart_prices[item][1] = buy_price / Settings::ITEM_SELL_PRICE_DIVISOR
end
end

View File

@@ -51,7 +51,7 @@ class Interpreter
when 134 then return command_134 # Change Save Access
when 135 then return command_135 # Change Menu Access
when 136 then return command_136 # Change Encounter
when 201 then return command_201 # Transfer Player
when 201 then return command_201 # Transfer Overrides
when 202 then return command_202 # Set Event Location
when 203 then return command_203 # Scroll Map
when 204 then return command_204 # Change Map Settings
@@ -121,16 +121,19 @@ class Interpreter
def command_dummy
return true
end
#-----------------------------------------------------------------------------
# * End Event
#-----------------------------------------------------------------------------
def command_end
@list = nil
end_follower_overrides
# If main map event and event ID are valid, unlock event
if @main && @event_id > 0 && $game_map.events[@event_id]
$game_map.events[@event_id].unlock
end
end
#-----------------------------------------------------------------------------
# * Command Skip
#-----------------------------------------------------------------------------
@@ -141,6 +144,7 @@ class Interpreter
@index += 1
end
end
#-----------------------------------------------------------------------------
# * Command If
#-----------------------------------------------------------------------------
@@ -151,6 +155,7 @@ class Interpreter
end
return command_skip
end
#-----------------------------------------------------------------------------
# * Show Text
#-----------------------------------------------------------------------------
@@ -158,7 +163,7 @@ class Interpreter
return false if $game_temp.message_window_showing
message = @list[@index].parameters[0]
message_end = ""
commands = nil
choices = nil
number_input_variable = nil
number_input_max_digits = nil
# Check the next command(s) for things to add on to this text
@@ -174,8 +179,8 @@ class Interpreter
when 101 # Show Text
message_end = "\1"
when 102 # Show Choices
commands = @list[next_index].parameters
@index = next_index
choices = setup_choices(@list[@index].parameters)
when 103 # Input Number
number_input_variable = @list[next_index].parameters[0]
number_input_max_digits = @list[next_index].parameters[1]
@@ -185,15 +190,11 @@ class Interpreter
end
# Translate the text
message = _MAPINTL($game_map.map_id, message)
# Display the text, with commands/number choosing if appropriate
# Display the text, with choices/number choosing if appropriate
@message_waiting = true # Lets parallel process events work while a message is displayed
if commands
cmd_texts = []
for cmd in commands[0]
cmd_texts.push(_MAPINTL($game_map.map_id, cmd))
end
command = pbMessage(message + message_end, cmd_texts, commands[1])
@branch[@list[@index].indent] = command
if choices
command = pbMessage(message + message_end, choices[0], choices[1])
@branch[@list[@index].indent] = choices[2][command] || command
elsif number_input_variable
params = ChooseNumberParams.new
params.setMaxDigits(number_input_max_digits)
@@ -206,37 +207,118 @@ class Interpreter
@message_waiting = false
return true
end
#-----------------------------------------------------------------------------
# * Show Choices
#-----------------------------------------------------------------------------
def command_102
choices = setup_choices(@list[@index].parameters)
@message_waiting = true
command = pbShowCommands(nil, @list[@index].parameters[0], @list[@index].parameters[1])
command = pbShowCommands(nil, choices[0], choices[1])
@message_waiting = false
@branch[@list[@index].indent] = command
@branch[@list[@index].indent] = choices[2][command] || command
Input.update # Must call Input.update again to avoid extra triggers
return true
end
def setup_choices(params)
# Get initial options
choices = params[0].clone
cancel_index = params[1]
# Clone @list so the original isn't modified
@list = Marshal.load(Marshal.dump(@list))
# Get more choices
@choice_branch_index = 4
ret = add_more_choices(choices, cancel_index, @index + 1, @list[@index].indent)
# Rename choices
ret[0].each_with_index { |choice, i| ret[0][i] = @renamed_choices[i] if @renamed_choices[i] }
@renamed_choices.clear
# Remove hidden choices
ret[2] = Array.new(ret[0].length) { |i| i }
@hidden_choices.each_with_index do |condition, i|
next if !condition
ret[0][i] = nil
ret[2][i] = nil
end
ret[0].compact!
ret[2].compact!
@hidden_choices.clear
# Translate choices
ret[0].map! { |ch| _MAPINTL($game_map.map_id, ch) }
return ret
end
def add_more_choices(choices, cancel_index, choice_index, indent)
# Find index of next command after the current Show Choices command
loop do
break if @list[choice_index].indent == indent && ![402, 403, 404].include?(@list[choice_index].code)
choice_index += 1
end
next_cmd = @list[choice_index]
# If the next command isn't another Show Choices, we're done
return [choices, cancel_index] if next_cmd.code != 102
# Add more choices
old_length = choices.length
choices += next_cmd.parameters[0]
# Update cancel option
if next_cmd.parameters[1] == 5 # Branch
cancel_index = choices.length + 1
@choice_branch_index = cancel_index - 1
elsif next_cmd.parameters[1] > 0 # A choice
cancel_index = old_length + next_cmd.parameters[1]
@choice_branch_index = -1
end
# Update first Show Choices command to include all options and result of cancelling
@list[@index].parameters[0] = choices
@list[@index].parameters[1] = cancel_index
# Find the "When" lines for this Show Choices command and update their index parameter
temp_index = choice_index + 1
loop do
break if @list[temp_index].indent == indent && ![402, 403, 404].include?(@list[temp_index].code)
if @list[temp_index].code == 402 && @list[temp_index].indent == indent
@list[temp_index].parameters[0] += old_length
end
temp_index += 1
end
# Delete the "Show Choices" line
@list.delete(next_cmd)
# Find more choices to add
return add_more_choices(choices, cancel_index, choice_index + 1, indent)
end
def hide_choice(number, condition = true)
@hidden_choices[number - 1] = condition
end
def rename_choice(number, new_name, condition = true)
return if !condition || nil_or_empty?(new_name)
@renamed_choices[number - 1] = new_name
end
#-----------------------------------------------------------------------------
# * When [**]
#-----------------------------------------------------------------------------
def command_402
# @parameters[0] is 0/1/2/3 for Choice 1/2/3/4 respectively
if @branch[@list[@index].indent] == @parameters[0]
@branch.delete(@list[@index].indent)
return true
end
return command_skip
end
#-----------------------------------------------------------------------------
# * When Cancel
#-----------------------------------------------------------------------------
def command_403
if @branch[@list[@index].indent] == 4
# @parameters[0] is 4 for "Branch"
if @branch[@list[@index].indent] == @choice_branch_index
@branch.delete(@list[@index].indent)
return true
end
return command_skip
end
#-----------------------------------------------------------------------------
# * Input Number
#-----------------------------------------------------------------------------
@@ -251,6 +333,7 @@ class Interpreter
@message_waiting = false
return true
end
#-----------------------------------------------------------------------------
# * Change Text Options
#-----------------------------------------------------------------------------
@@ -260,26 +343,22 @@ class Interpreter
$game_system.message_frame = @parameters[1]
return true
end
#-----------------------------------------------------------------------------
# * Button Input Processing
#-----------------------------------------------------------------------------
def pbButtonInputProcessing(variable_number = 0, timeout_frames = 0)
ret = 0
timer = timeout_frames * Graphics.frame_rate / 20
timer_start = System.uptime
loop do
Graphics.update
Input.update
pbUpdateSceneMap
# Check for input and break if there is one
for i in 1..18
ret = i if Input.trigger?(i)
end
(1..18).each { |i| ret = i if Input.trigger?(i) }
break if ret != 0
# Count down the timer and break if it runs out
if timeout_frames > 0
timer -= 1
break if timer <= 0
end
# Break if the timer runs out
break if timeout_frames > 0 && System.uptime - timer_start >= timeout_frames / 20.0
end
Input.update
if variable_number && variable_number > 0
@@ -297,13 +376,16 @@ class Interpreter
@index += 1
return true
end
#-----------------------------------------------------------------------------
# * Wait
#-----------------------------------------------------------------------------
def command_106
@wait_count = @parameters[0] * Graphics.frame_rate / 20
@wait_count = @parameters[0] / 20.0
@wait_start = System.uptime
return true
end
#-----------------------------------------------------------------------------
# * Conditional Branch
#-----------------------------------------------------------------------------
@@ -318,8 +400,22 @@ class Interpreter
result = ($game_switches[@parameters[1]] == (@parameters[2] == 0))
end
when 1 # variable
value1 = $game_variables[@parameters[1]]
value2 = (@parameters[2] == 0) ? @parameters[3] : $game_variables[@parameters[3]]
variable1_name = $data_system.variables[@parameters[1]]
if variable1_name && variable1_name[/^s\:/]
value1 = eval($~.post_match)
else
value1 = $game_variables[@parameters[1]]
end
if @parameters[2] == 0
value2 = @parameters[3]
else
variable2_name = $data_system.variables[@parameters[3]]
if variable2_name && variable2_name[/^s\:/]
value2 = eval($~.post_match)
else
value2 = $game_variables[@parameters[3]]
end
end
case @parameters[4]
when 0 then result = (value1 == value2)
when 1 then result = (value1 >= value2)
@@ -334,8 +430,8 @@ class Interpreter
result = ($game_self_switches[key] == (@parameters[2] == 0))
end
when 3 # timer
if $game_system.timer_working
sec = $game_system.timer / Graphics.frame_rate
if $game_system.timer_start
sec = $game_system.timer
result = (@parameters[2] == 0) ? (sec >= @parameters[1]) : (sec <= @parameters[1])
end
# when 4, 5 # actor, enemy
@@ -343,7 +439,7 @@ class Interpreter
character = get_character(@parameters[1])
result = (character.direction == @parameters[2]) if character
when 7 # gold
gold = $Trainer.money
gold = $player.money
result = (@parameters[2] == 0) ? (gold >= @parameters[1]) : (gold <= @parameters[1])
# when 8, 9, 10 # item, weapon, armor
when 11 # button
@@ -359,6 +455,7 @@ class Interpreter
end
return command_skip
end
#-----------------------------------------------------------------------------
# * Else
#-----------------------------------------------------------------------------
@@ -369,12 +466,14 @@ class Interpreter
end
return command_skip
end
#-----------------------------------------------------------------------------
# * Loop
#-----------------------------------------------------------------------------
def command_112
return true
end
#-----------------------------------------------------------------------------
# * Repeat Above
#-----------------------------------------------------------------------------
@@ -385,6 +484,7 @@ class Interpreter
return true if @list[@index].indent == indent
end
end
#-----------------------------------------------------------------------------
# * Break Loop
#-----------------------------------------------------------------------------
@@ -401,6 +501,7 @@ class Interpreter
end
end
end
#-----------------------------------------------------------------------------
# * Exit Event Processing
#-----------------------------------------------------------------------------
@@ -408,17 +509,19 @@ class Interpreter
command_end
return true
end
#-----------------------------------------------------------------------------
# * Erase Event
#-----------------------------------------------------------------------------
def command_116
if @event_id > 0
$game_map.events[@event_id].erase if $game_map.events[@event_id]
$PokemonMap.addErasedEvent(@event_id) if $PokemonMap
$game_map.events[@event_id]&.erase
$PokemonMap&.addErasedEvent(@event_id)
end
@index += 1
return false
end
#-----------------------------------------------------------------------------
# * Call Common Event
#-----------------------------------------------------------------------------
@@ -430,12 +533,14 @@ class Interpreter
end
return true
end
#-----------------------------------------------------------------------------
# * Label
#-----------------------------------------------------------------------------
def command_118
return true
end
#-----------------------------------------------------------------------------
# * Jump to Label
#-----------------------------------------------------------------------------
@@ -454,12 +559,13 @@ class Interpreter
temp_index += 1
end
end
#-----------------------------------------------------------------------------
# * Control Switches
#-----------------------------------------------------------------------------
def command_121
should_refresh = false
for i in @parameters[0]..@parameters[1]
(@parameters[0]..@parameters[1]).each do |i|
next if $game_switches[i] == (@parameters[2] == 0)
$game_switches[i] = (@parameters[2] == 0)
should_refresh = true
@@ -468,6 +574,7 @@ class Interpreter
$game_map.need_refresh = true if should_refresh
return true
end
#-----------------------------------------------------------------------------
# * Control Variables
#-----------------------------------------------------------------------------
@@ -495,43 +602,44 @@ class Interpreter
end
when 7 # other
case @parameters[4]
when 0 then value = $game_map.map_id # map ID
when 1 then value = $Trainer.pokemon_party.length # party members
when 2 then value = $Trainer.money # gold
# when 3 # steps
when 4 then value = Graphics.frame_count / Graphics.frame_rate # play time
when 5 then value = $game_system.timer / Graphics.frame_rate # timer
when 6 then value = $game_system.save_count # save count
when 0 then value = $game_map.map_id # map ID
when 1 then value = $player.pokemon_count # party members
when 2 then value = $player.money # gold
when 3 then value = $stats.distance_moved # steps
when 4 then value = $stats.play_time # play time
when 5 then value = $game_system.timer # timer
when 6 then value = $game_system.save_count # save count
end
end
# Apply value and operation to all specified game variables
for i in @parameters[0]..@parameters[1]
(@parameters[0]..@parameters[1]).each do |i|
case @parameters[2]
when 0 # set
next if $game_variables[i] == value
$game_variables[i] = value
when 1 # add
next if $game_variables[i] >= 99999999
next if $game_variables[i] >= 99_999_999
$game_variables[i] += value
when 2 # subtract
next if $game_variables[i] <= -99999999
next if $game_variables[i] <= -99_999_999
$game_variables[i] -= value
when 3 # multiply
next if value == 1
$game_variables[i] *= value
when 4 # divide
next if value == 1 || value == 0
next if [0, 1].include?(value)
$game_variables[i] /= value
when 5 # remainder
next if value == 1 || value == 0
next if [0, 1].include?(value)
$game_variables[i] %= value
end
$game_variables[i] = 99999999 if $game_variables[i] > 99999999
$game_variables[i] = -99999999 if $game_variables[i] < -99999999
$game_variables[i] = 99_999_999 if $game_variables[i] > 99_999_999
$game_variables[i] = -99_999_999 if $game_variables[i] < -99_999_999
$game_map.need_refresh = true
end
return true
end
#-----------------------------------------------------------------------------
# * Control Self Switch
#-----------------------------------------------------------------------------
@@ -546,21 +654,23 @@ class Interpreter
end
return true
end
#-----------------------------------------------------------------------------
# * Control Timer
#-----------------------------------------------------------------------------
def command_124
$game_system.timer_working = (@parameters[0] == 0)
$game_system.timer = @parameters[1] * Graphics.frame_rate if @parameters[0] == 0
$game_system.timer_start = (@parameters[0] == 0) ? $stats.play_time : nil
$game_system.timer_duration = @parameters[1] if @parameters[0] == 0
return true
end
#-----------------------------------------------------------------------------
# * Change Gold
#-----------------------------------------------------------------------------
def command_125
value = (@parameters[1] == 0) ? @parameters[2] : $game_variables[@parameters[2]]
value = -value if @parameters[0] == 1 # Decrease
$Trainer.money += value
$player.money += value
return true
end
@@ -568,11 +678,12 @@ class Interpreter
def command_127; command_dummy; end # Change Weapons
def command_128; command_dummy; end # Change Armor
def command_129; command_dummy; end # Change Party Member
#-----------------------------------------------------------------------------
# * Change Windowskin
#-----------------------------------------------------------------------------
def command_131
for i in 0...Settings::SPEECH_WINDOWSKINS.length
Settings::SPEECH_WINDOWSKINS.length.times do |i|
next if Settings::SPEECH_WINDOWSKINS[i] != @parameters[0]
$PokemonSystem.textskin = i
MessageConfig.pbSetSpeechFrame("Graphics/Windowskins/" + Settings::SPEECH_WINDOWSKINS[i])
@@ -580,6 +691,7 @@ class Interpreter
end
return true
end
#-----------------------------------------------------------------------------
# * Change Battle BGM
#-----------------------------------------------------------------------------
@@ -587,13 +699,12 @@ class Interpreter
($PokemonGlobal.nextBattleBGM = @parameters[0]) ? @parameters[0].clone : nil
return true
end
#-----------------------------------------------------------------------------
# * Change Battle End ME
#-----------------------------------------------------------------------------
def command_133
($PokemonGlobal.nextBattleME = @parameters[0]) ? @parameters[0].clone : nil
return true
end
def command_133; command_dummy; end
#-----------------------------------------------------------------------------
# * Change Save Access
#-----------------------------------------------------------------------------
@@ -601,6 +712,7 @@ class Interpreter
$game_system.save_disabled = (@parameters[0] == 0)
return true
end
#-----------------------------------------------------------------------------
# * Change Menu Access
#-----------------------------------------------------------------------------
@@ -608,6 +720,7 @@ class Interpreter
$game_system.menu_disabled = (@parameters[0] == 0)
return true
end
#-----------------------------------------------------------------------------
# * Change Encounter
#-----------------------------------------------------------------------------
@@ -616,8 +729,9 @@ class Interpreter
$game_player.make_encounter_count
return true
end
#-----------------------------------------------------------------------------
# * Transfer Player
# * Transfer Overrides
#-----------------------------------------------------------------------------
def command_201
return true if $game_temp.in_battle
@@ -630,13 +744,12 @@ class Interpreter
$game_temp.player_new_map_id = @parameters[1]
$game_temp.player_new_x = @parameters[2]
$game_temp.player_new_y = @parameters[3]
$game_temp.player_new_direction = @parameters[4]
else # Appoint with variables
$game_temp.player_new_map_id = $game_variables[@parameters[1]]
$game_temp.player_new_x = $game_variables[@parameters[2]]
$game_temp.player_new_y = $game_variables[@parameters[3]]
$game_temp.player_new_direction = @parameters[4]
end
$game_temp.player_new_direction = @parameters[4]
@index += 1
# If transition happens with a fade, do the fade
if @parameters[5] == 0
@@ -646,6 +759,7 @@ class Interpreter
end
return false
end
#-----------------------------------------------------------------------------
# * Set Event Location
#-----------------------------------------------------------------------------
@@ -654,9 +768,10 @@ class Interpreter
character = get_character(@parameters[0])
return true if character.nil?
# Move the character
if @parameters[1] == 0 # Direct appointment
case @parameters[1]
when 0 # Direct appointment
character.moveto(@parameters[2], @parameters[3])
elsif @parameters[1] == 1 # Appoint with variables
when 1 # Appoint with variables
character.moveto($game_variables[@parameters[2]], $game_variables[@parameters[3]])
else # Exchange with another event
character2 = get_character(@parameters[2])
@@ -676,6 +791,7 @@ class Interpreter
end
return true
end
#-----------------------------------------------------------------------------
# * Scroll Map
#-----------------------------------------------------------------------------
@@ -685,6 +801,7 @@ class Interpreter
$game_map.start_scroll(@parameters[0], @parameters[1], @parameters[2])
return true
end
#-----------------------------------------------------------------------------
# * Change Map Settings
#-----------------------------------------------------------------------------
@@ -707,29 +824,38 @@ class Interpreter
end
return true
end
#-----------------------------------------------------------------------------
# * Change Fog Color Tone
#-----------------------------------------------------------------------------
def command_205
$game_map.start_fog_tone_change(@parameters[0], @parameters[1] * Graphics.frame_rate / 20)
$game_map.start_fog_tone_change(@parameters[0], @parameters[1])
return true
end
#-----------------------------------------------------------------------------
# * Change Fog Opacity
#-----------------------------------------------------------------------------
def command_206
$game_map.start_fog_opacity_change(@parameters[0], @parameters[1] * Graphics.frame_rate / 20)
$game_map.start_fog_opacity_change(@parameters[0], @parameters[1])
return true
end
#-----------------------------------------------------------------------------
# * Show Animation
#-----------------------------------------------------------------------------
def command_207
character = get_character(@parameters[0])
if @follower_animation
character = Followers.get(@follower_animation_id)
@follower_animation = false
@follower_animation_id = nil
end
return true if character.nil?
character.animation_id = @parameters[1]
return true
end
#-----------------------------------------------------------------------------
# * Change Transparent Flag
#-----------------------------------------------------------------------------
@@ -737,15 +863,22 @@ class Interpreter
$game_player.transparent = (@parameters[0] == 0)
return true
end
#-----------------------------------------------------------------------------
# * Set Move Route
#-----------------------------------------------------------------------------
def command_209
character = get_character(@parameters[0])
if @follower_move_route
character = Followers.get(@follower_move_route_id)
@follower_move_route = false
@follower_move_route_id = nil
end
return true if character.nil?
character.force_move_route(@parameters[1])
return true
end
#-----------------------------------------------------------------------------
# * Wait for Move's Completion
#-----------------------------------------------------------------------------
@@ -753,6 +886,7 @@ class Interpreter
@move_route_waiting = true if !$game_temp.in_battle
return true
end
#-----------------------------------------------------------------------------
# * Prepare for Transition
#-----------------------------------------------------------------------------
@@ -761,6 +895,7 @@ class Interpreter
Graphics.freeze
return true
end
#-----------------------------------------------------------------------------
# * Execute Transition
#-----------------------------------------------------------------------------
@@ -771,27 +906,31 @@ class Interpreter
@index += 1
return false
end
#-----------------------------------------------------------------------------
# * Change Screen Color Tone
#-----------------------------------------------------------------------------
def command_223
$game_screen.start_tone_change(@parameters[0], @parameters[1] * Graphics.frame_rate / 20)
$game_screen.start_tone_change(@parameters[0], @parameters[1])
return true
end
#-----------------------------------------------------------------------------
# * Screen Flash
#-----------------------------------------------------------------------------
def command_224
$game_screen.start_flash(@parameters[0], @parameters[1] * Graphics.frame_rate / 20)
$game_screen.start_flash(@parameters[0], @parameters[1])
return true
end
#-----------------------------------------------------------------------------
# * Screen Shake
#-----------------------------------------------------------------------------
def command_225
$game_screen.start_shake(@parameters[0], @parameters[1], @parameters[2] * Graphics.frame_rate / 20)
$game_screen.start_shake(@parameters[0], @parameters[1], @parameters[2])
return true
end
#-----------------------------------------------------------------------------
# * Show Picture
#-----------------------------------------------------------------------------
@@ -805,9 +944,10 @@ class Interpreter
y = $game_variables[@parameters[5]]
end
$game_screen.pictures[number].show(@parameters[1], @parameters[2],
x, y, @parameters[6], @parameters[7], @parameters[8], @parameters[9])
x, y, @parameters[6], @parameters[7], @parameters[8], @parameters[9])
return true
end
#-----------------------------------------------------------------------------
# * Move Picture
#-----------------------------------------------------------------------------
@@ -820,10 +960,12 @@ class Interpreter
x = $game_variables[@parameters[4]]
y = $game_variables[@parameters[5]]
end
$game_screen.pictures[number].move(@parameters[1] * Graphics.frame_rate / 20,
@parameters[2], x, y, @parameters[6], @parameters[7], @parameters[8], @parameters[9])
$game_screen.pictures[number].move(@parameters[1], @parameters[2], x, y,
@parameters[6], @parameters[7],
@parameters[8], @parameters[9])
return true
end
#-----------------------------------------------------------------------------
# * Rotate Picture
#-----------------------------------------------------------------------------
@@ -832,15 +974,16 @@ class Interpreter
$game_screen.pictures[number].rotate(@parameters[1])
return true
end
#-----------------------------------------------------------------------------
# * Change Picture Color Tone
#-----------------------------------------------------------------------------
def command_234
number = @parameters[0] + ($game_temp.in_battle ? 50 : 0)
$game_screen.pictures[number].start_tone_change(@parameters[1],
@parameters[2] * Graphics.frame_rate / 20)
$game_screen.pictures[number].start_tone_change(@parameters[1], @parameters[2])
return true
end
#-----------------------------------------------------------------------------
# * Erase Picture
#-----------------------------------------------------------------------------
@@ -849,6 +992,7 @@ class Interpreter
$game_screen.pictures[number].erase
return true
end
#-----------------------------------------------------------------------------
# * Set Weather Effects
#-----------------------------------------------------------------------------
@@ -856,6 +1000,7 @@ class Interpreter
$game_screen.weather(@parameters[0], @parameters[1], @parameters[2])
return true
end
#-----------------------------------------------------------------------------
# * Play BGM
#-----------------------------------------------------------------------------
@@ -863,6 +1008,7 @@ class Interpreter
pbBGMPlay(@parameters[0])
return true
end
#-----------------------------------------------------------------------------
# * Fade Out BGM
#-----------------------------------------------------------------------------
@@ -870,6 +1016,7 @@ class Interpreter
pbBGMFade(@parameters[0])
return true
end
#-----------------------------------------------------------------------------
# * Play BGS
#-----------------------------------------------------------------------------
@@ -877,6 +1024,7 @@ class Interpreter
pbBGSPlay(@parameters[0])
return true
end
#-----------------------------------------------------------------------------
# * Fade Out BGS
#-----------------------------------------------------------------------------
@@ -884,6 +1032,7 @@ class Interpreter
pbBGSFade(@parameters[0])
return true
end
#-----------------------------------------------------------------------------
# * Memorize BGM/BGS
#-----------------------------------------------------------------------------
@@ -892,6 +1041,7 @@ class Interpreter
$game_system.bgs_memorize
return true
end
#-----------------------------------------------------------------------------
# * Restore BGM/BGS
#-----------------------------------------------------------------------------
@@ -900,6 +1050,7 @@ class Interpreter
$game_system.bgs_restore
return true
end
#-----------------------------------------------------------------------------
# * Play ME
#-----------------------------------------------------------------------------
@@ -907,6 +1058,7 @@ class Interpreter
pbMEPlay(@parameters[0])
return true
end
#-----------------------------------------------------------------------------
# * Play SE
#-----------------------------------------------------------------------------
@@ -914,6 +1066,7 @@ class Interpreter
pbSEPlay(@parameters[0])
return true
end
#-----------------------------------------------------------------------------
# * Stop SE
#-----------------------------------------------------------------------------
@@ -927,23 +1080,25 @@ class Interpreter
def command_602; command_if(1); end # If Escape
def command_603; command_if(2); end # If Lose
def command_302; command_dummy; end # Shop Processing
#-----------------------------------------------------------------------------
# * Name Input Processing
#-----------------------------------------------------------------------------
def command_303
if $Trainer
$Trainer.name = pbEnterPlayerName(_INTL("Your name?"), 1, @parameters[1], $Trainer.name)
if $player
$player.name = pbEnterPlayerName(_INTL("Your name?"), 1, @parameters[1], $player.name)
return true
end
if $game_actors && $data_actors && $data_actors[@parameters[0]] != nil
if $game_actors && $data_actors && $data_actors[@parameters[0]]
$game_temp.battle_abort = true
pbFadeOutIn {
pbFadeOutIn do
sscene = PokemonEntryScene.new
sscreen = PokemonEntry.new(sscene)
$game_actors[@parameters[0]].name = sscreen.pbStartScreen(
_INTL("Enter {1}'s name.", $game_actors[@parameters[0]].name),
1, @parameters[1], $game_actors[@parameters[0]].name)
}
_INTL("Enter {1}'s name.", $game_actors[@parameters[0]].name),
1, @parameters[1], $game_actors[@parameters[0]].name
)
end
end
return true
end
@@ -951,11 +1106,18 @@ class Interpreter
def command_311; command_dummy; end # Change HP
def command_312; command_dummy; end # Change SP
def command_313; command_dummy; end # Change State
#-----------------------------------------------------------------------------
# * Recover All
#-----------------------------------------------------------------------------
def command_314
$Trainer.heal_party if @parameters[0] == 0
if @parameters[0] == 0
if Settings::HEAL_STORED_POKEMON # No need to heal stored Pokémon
$player.heal_party
else
pbEachPokemon { |pkmn, box| pkmn.heal } # Includes party Pokémon
end
end
return true
end
@@ -977,6 +1139,7 @@ class Interpreter
def command_338; command_dummy; end # Deal Damage
def command_339; command_dummy; end # Force Action
def command_340; command_dummy; end # Abort Battle
#-----------------------------------------------------------------------------
# * Call Menu Screen
#-----------------------------------------------------------------------------
@@ -985,15 +1148,15 @@ class Interpreter
@index += 1
return false
end
#-----------------------------------------------------------------------------
# * Call Save Screen
#-----------------------------------------------------------------------------
def command_352
scene = PokemonSave_Scene.new
screen = PokemonSaveScreen.new(scene)
screen.pbSaveScreen
pbFadeOutIn { UI::Save.new.main }
return true
end
#-----------------------------------------------------------------------------
# * Game Over
#-----------------------------------------------------------------------------
@@ -1002,13 +1165,15 @@ class Interpreter
pbBGSFade(1.0)
pbFadeOutIn { pbStartOver(true) }
end
#-----------------------------------------------------------------------------
# * Return to Title Screen
#-----------------------------------------------------------------------------
def command_354
$game_temp.to_title = true
$game_temp.title_screen_calling = true
return false
end
#-----------------------------------------------------------------------------
# * Script
#-----------------------------------------------------------------------------
@@ -1017,7 +1182,7 @@ class Interpreter
# Look for more script commands or a continuation of one, and add them to script
loop do
break if ![355, 655].include?(@list[@index + 1].code)
script += @list[@index+1].parameters[0] + "\n"
script += @list[@index + 1].parameters[0] + "\n"
@index += 1
end
# Run the script

View File

@@ -13,21 +13,15 @@ class Event
end
# Removes an event handler procedure from the event.
def -(method)
for i in 0...@callbacks.length
next if @callbacks[i]!=method
@callbacks.delete_at(i)
break
end
def -(other)
@callbacks.delete(other)
return self
end
# Adds an event handler procedure from the event.
def +(method)
for i in 0...@callbacks.length
return self if @callbacks[i]==method
end
@callbacks.push(method)
def +(other)
return self if @callbacks.include?(other)
@callbacks.push(other)
return self
end
@@ -44,13 +38,13 @@ class Event
# proc { |sender,params| } where params is an array of the other parameters, and
# proc { |sender,arg0,arg1,...| }
def trigger(*arg)
arglist = arg[1,arg.length]
for callback in @callbacks
if callback.arity>2 && arg.length==callback.arity
arglist = arg[1, arg.length]
@callbacks.each do |callback|
if callback.arity > 2 && arg.length == callback.arity
# Retrofitted for callbacks that take three or more arguments
callback.call(*arg)
else
callback.call(arg[0],arglist)
callback.call(arg[0], arglist)
end
end
end
@@ -59,16 +53,164 @@ class Event
# by the code where the event occurred. The first argument is the sender of
# the event, the other arguments are the event's parameters.
def trigger2(*arg)
for callback in @callbacks
@callbacks.each do |callback|
callback.call(*arg)
end
end
end
#===============================================================================
#
# Same as class Event, but each registered proc has a name (a symbol) so it can
# be referenced individually.
#===============================================================================
class NamedEvent
def initialize
@callbacks = {}
end
# Adds an event handler procedure from the event.
def add(key, proc)
@callbacks[key] = proc
end
# Removes an event handler procedure from the event.
def remove(key)
@callbacks.delete(key)
end
# Clears the event of event handlers.
def clear
@callbacks.clear
end
# Triggers the event and calls all its event handlers. Normally called only
# by the code where the event occurred.
def trigger(*args)
@callbacks.each_value { |callback| callback.call(*args) }
end
end
#===============================================================================
# A class that stores code that can be triggered. Each piece of code has an
# associated ID, which can be anything that can be used as a key in a hash.
#===============================================================================
class HandlerHash
def initialize
@hash = {}
end
def [](id)
return @hash[id] if id && @hash[id]
return nil
end
def keys
return @hash.keys
end
def add(id, handler = nil, &handlerBlock)
if ![Proc, Hash].include?(handler.class) && !block_given?
raise ArgumentError, "#{self.class.name} for #{id.inspect} has no valid handler (#{handler.inspect} was given)"
end
@hash[id] = handler || handlerBlock if id && !id.empty?
end
def copy(src, *dests)
handler = self[src]
return if !handler
dests.each { |dest| add(dest, handler) }
end
def remove(key)
@hash.delete(key)
end
def clear
@hash.clear
end
def each
@hash.each_pair { |key, value| yield key, value }
end
def keys
return @hash.keys.clone
end
# NOTE: The call does not pass id as a parameter to the proc/block.
def trigger(id, *args)
handler = self[id]
return handler&.call(*args)
end
end
#===============================================================================
# A stripped-down version of class HandlerHash which only deals with IDs that
# are symbols. Also contains an add_ifs hash for code that applies to multiple
# IDs (determined by its condition proc).
#===============================================================================
class HandlerHashSymbol
def initialize
@hash = {}
@add_ifs = {}
end
def [](sym)
sym = sym.id if !sym.is_a?(Symbol) && sym.respond_to?("id")
return @hash[sym] if sym && @hash[sym]
@add_ifs.each_value do |add_if|
return add_if[1] if add_if[0].call(sym)
end
return nil
end
def keys
return @hash.keys
end
def add(sym, handler = nil, &handlerBlock)
if ![Proc, Hash].include?(handler.class) && !block_given?
raise ArgumentError, "#{self.class.name} for #{sym.inspect} has no valid handler (#{handler.inspect} was given)"
end
@hash[sym] = handler || handlerBlock if sym
end
def addIf(sym, conditionProc, handler = nil, &handlerBlock)
if ![Proc, Hash].include?(handler.class) && !block_given?
raise ArgumentError, "addIf call for #{sym} in #{self.class.name} has no valid handler (#{handler.inspect} was given)"
end
@add_ifs[sym] = [conditionProc, handler || handlerBlock]
end
def copy(src, *dests)
handler = self[src]
return if !handler
dests.each { |dest| add(dest, handler) }
end
def remove(key)
@hash.delete(key)
end
def clear
@hash.clear
@add_ifs.clear
end
def trigger(sym, *args)
sym = sym.id if !sym.is_a?(Symbol) && sym.respond_to?("id")
handler = self[sym]
return handler&.call(sym, *args)
end
end
#===============================================================================
# A specialised version of class HandlerHash which only deals with IDs that are
# constants in a particular class or module. That class or module must be
# defined when creating an instance of this class.
# Unused.
#===============================================================================
class HandlerHashEnum
def initialize(mod)
@mod = mod
@hash = {}
@@ -76,6 +218,25 @@ class HandlerHash
@symbolCache = {}
end
# 'sym' can be an ID or symbol.
def [](sym)
id = fromSymbol(sym)
ret = nil
ret = @hash[id] if id && @hash[id] # Real ID from the item
symbol = toSymbol(sym)
ret = @hash[symbol] if symbol && @hash[symbol] # Symbol or string
unless ret
@addIfs.each do |addif|
return addif[1] if addif[0].call(id)
end
end
return ret
end
def keys
return @hash.keys
end
def fromSymbol(sym)
return sym unless sym.is_a?(Symbol) || sym.is_a?(String)
mod = Object.const_get(@mod) rescue nil
@@ -89,8 +250,8 @@ class HandlerHash
return ret if ret
mod = Object.const_get(@mod) rescue nil
return nil if !mod
for key in mod.constants
next if mod.const_get(key)!=sym
mod.constants.each do |key|
next if mod.const_get(key) != sym
ret = key.to_sym
@symbolCache[sym] = ret
break
@@ -98,15 +259,9 @@ class HandlerHash
return ret
end
def addIf(conditionProc,handler=nil,&handlerBlock)
if ![Proc,Hash].include?(handler.class) && !block_given?
raise ArgumentError, "addIf call for #{self.class.name} has no valid handler (#{handler.inspect} was given)"
end
@addIfs.push([conditionProc,handler || handlerBlock])
end
def add(sym,handler=nil,&handlerBlock) # 'sym' can be an ID or symbol
if ![Proc,Hash].include?(handler.class) && !block_given?
# 'sym' can be an ID or symbol.
def add(sym, handler = nil, &handlerBlock)
if ![Proc, Hash].include?(handler.class) && !block_given?
raise ArgumentError, "#{self.class.name} for #{sym.inspect} has no valid handler (#{handler.inspect} was given)"
end
id = fromSymbol(sym)
@@ -115,126 +270,6 @@ class HandlerHash
@hash[symbol] = handler || handlerBlock if symbol
end
def copy(src,*dests)
handler = self[src]
if handler
for dest in dests
self.add(dest,handler)
end
end
end
def [](sym) # 'sym' can be an ID or symbol
id = fromSymbol(sym)
ret = nil
ret = @hash[id] if id && @hash[id] # Real ID from the item
symbol = toSymbol(sym)
ret = @hash[symbol] if symbol && @hash[symbol] # Symbol or string
unless ret
for addif in @addIfs
return addif[1] if addif[0].call(id)
end
end
return ret
end
def trigger(sym,*args)
handler = self[sym]
return (handler) ? handler.call(fromSymbol(sym),*args) : nil
end
def clear
@hash.clear
end
end
#===============================================================================
# A stripped-down version of class HandlerHash which only deals with symbols and
# doesn't care about whether those symbols actually relate to a defined thing.
#===============================================================================
class HandlerHash2
def initialize
@hash = {}
@add_ifs = []
end
def [](sym)
sym = sym.id if !sym.is_a?(Symbol) && sym.respond_to?("id")
return @hash[sym] if sym && @hash[sym]
for add_if in @add_ifs
return add_if[1] if add_if[0].call(sym)
end
return nil
end
def addIf(conditionProc, handler = nil, &handlerBlock)
if ![Proc, Hash].include?(handler.class) && !block_given?
raise ArgumentError, "addIf call for #{self.class.name} has no valid handler (#{handler.inspect} was given)"
end
@add_ifs.push([conditionProc, handler || handlerBlock])
end
def add(sym, handler = nil, &handlerBlock)
if ![Proc, Hash].include?(handler.class) && !block_given?
raise ArgumentError, "#{self.class.name} for #{sym.inspect} has no valid handler (#{handler.inspect} was given)"
end
@hash[sym] = handler || handlerBlock if sym
end
def copy(src, *dests)
handler = self[src]
return if !handler
for dest in dests
self.add(dest, handler)
end
end
def clear
@hash.clear
end
def trigger(sym, *args)
sym = sym.id if !sym.is_a?(Symbol) && sym.respond_to?("id")
handler = self[sym]
return (handler) ? handler.call(sym, *args) : nil
end
end
#===============================================================================
# An even more stripped down version of class HandlerHash which just takes
# hashes with keys, no matter what the keys are.
#===============================================================================
class HandlerHashBasic
def initialize
@ordered_keys = []
@hash = {}
@addIfs = []
end
def [](entry)
ret = nil
ret = @hash[entry] if entry && @hash[entry]
unless ret
for addif in @addIfs
return addif[1] if addif[0].call(entry)
end
end
return ret
end
def each
@ordered_keys.each { |key| yield key, @hash[key] }
end
def add(entry, handler = nil, &handlerBlock)
if ![Proc,Hash].include?(handler.class) && !block_given?
raise ArgumentError, "#{self.class.name} for #{entry.inspect} has no valid handler (#{handler.inspect} was given)"
end
return if !entry || entry.empty?
@ordered_keys.push(entry) if !@ordered_keys.include?(entry)
@hash[entry] = handler || handlerBlock
end
def addIf(conditionProc, handler = nil, &handlerBlock)
if ![Proc, Hash].include?(handler.class) && !block_given?
raise ArgumentError, "addIf call for #{self.class.name} has no valid handler (#{handler.inspect} was given)"
@@ -250,26 +285,26 @@ class HandlerHashBasic
def clear
@hash.clear
@ordered_keys.clear
@addIfs.clear
end
def trigger(entry, *args)
handler = self[entry]
return (handler) ? handler.call(*args) : nil
def trigger(sym, *args)
handler = self[sym]
return (handler) ? handler.call(fromSymbol(sym), *args) : nil
end
end
#===============================================================================
#
#===============================================================================
class SpeciesHandlerHash < HandlerHash2
class SpeciesHandlerHash < HandlerHashSymbol
end
class AbilityHandlerHash < HandlerHash2
class AbilityHandlerHash < HandlerHashSymbol
end
class ItemHandlerHash < HandlerHash2
class ItemHandlerHash < HandlerHashSymbol
end
class MoveHandlerHash < HandlerHash2
class MoveHandlerHash < HandlerHashSymbol
end

View File

@@ -0,0 +1,173 @@
#===============================================================================
# This module stores events that can happen during the game. A procedure can
# subscribe to an event by adding itself to the event. It will then be called
# whenever the event occurs. Existing events are:
#-------------------------------------------------------------------------------
# :on_game_map_setup - When a Game_Map is set up. Typically changes map data.
# :on_new_spriteset_map - When a Spriteset_Map is created. Adds more things to
# show in the overworld.
# :on_frame_update - Once per frame. Various frame/time counters.
# :on_leave_map - When leaving a map. End weather/expired effects.
# :on_enter_map - Upon entering a new map. Set up new effects, end expired
# effects.
# :on_map_or_spriteset_change - Upon entering a new map or when spriteset was
# made. Show things on-screen.
#-------------------------------------------------------------------------------
# :on_player_change_direction - When the player turns in a different direction.
# :on_leave_tile - When any event or the player starts to move from a tile.
# :on_step_taken - When any event or the player finishes a step.
# :on_player_step_taken - When the player finishes a step/ends surfing, except
# as part of a move route. Step-based counters.
# :on_player_step_taken_can_transfer - When the player finishes a step/ends
# surfing, except as part of a move route. Step-based effects that can
# transfer the player elsewhere.
# :on_player_interact - When the player presses the Use button in the
# overworld.
#-------------------------------------------------------------------------------
# :on_trainer_load - When an NPCTrainer is generated (to battle against or as
# a registered partner). Various modifications to that trainer and their
# Pokémon.
# :on_wild_species_chosen - When a species/level have been chosen for a wild
# encounter. Changes the species/level (e.g. roamer, Poké Radar chain).
# :on_wild_pokemon_created - When a Pokemon object has been created for a wild
# encounter. Various modifications to that Pokémon.
# :on_calling_wild_battle - When a wild battle is called. Prevents that wild
# battle and instead starts a different kind of battle (e.g. Safari Zone).
# :on_start_battle - Just before a battle starts. Memorize/reset information
# about party Pokémon, which is used after battle for evolution checks.
# :on_end_battle - Just after a battle ends. Evolution checks, Pickup/Honey
# Gather, blacking out.
# :on_wild_battle_end - After a wild battle. Updates Poké Radar chain info.
#===============================================================================
module EventHandlers
@@events = {}
module_function
# Add a named callback for the given event.
def add(event, key, proc)
@@events[event] = NamedEvent.new if !@@events.has_key?(event)
@@events[event].add(key, proc)
end
# Remove a named callback from the given event.
def remove(event, key)
@@events[event]&.remove(key)
end
# Clear all callbacks for the given event.
def clear(key)
@@events[key]&.clear
end
# Trigger all callbacks from an Event if it has been defined.
def trigger(event, *args)
return @@events[event]&.trigger(*args)
end
end
#===============================================================================
# This module stores the contents of various menus. Each command in a menu is a
# hash of data (containing its name, relative order, code to run when chosen,
# etc.).
# Menus that use this module are:
#-------------------------------------------------------------------------------
# Pause menu
# Party screen main interact menu
# Pokégear main menu
# Options screen
# PC main menu
# Various debug menus (main, Pokémon, battle, battle Pokémon)
#===============================================================================
module MenuHandlers
@@handlers = {}
module_function
def add(menu, option, hash)
@@handlers[menu] = HandlerHash.new if !@@handlers.has_key?(menu)
@@handlers[menu].add(option, hash)
end
def remove(menu, option)
@@handlers[menu]&.remove(option)
end
def clear(menu)
@@handlers[menu]&.clear
end
def get(menu, option)
return @@handlers[menu][option]
end
def each(menu)
return if !@@handlers.has_key?(menu)
@@handlers[menu].each { |option, hash| yield option, hash }
end
def each_available(menu, *args)
return if !@@handlers.has_key?(menu)
options = @@handlers[menu]
keys = options.keys
sorted_keys = keys.sort_by { |option| options[option]["order"] || keys.index(option) }
sorted_keys.each do |option|
hash = options[option]
next if hash["condition"] && !hash["condition"].call(*args)
if hash["multi_options"]
extra_options = hash["multi_options"].call(*args)
if extra_options && extra_options.length > 0
if extra_options[0].is_a?(Array)
extra_options.each { |opt| yield *opt }
else
yield *extra_options
end
end
next
end
if hash["name"].is_a?(Proc)
name = hash["name"].call(*args)
else
name = _INTL(hash["name"])
end
yield option, hash, name
end
end
def call(menu, option, function, *args)
option_hash = @@handlers[menu][option]
return nil if !option_hash || !option_hash[function]
return option_hash[function].call(*args)
end
end
#===============================================================================
#
#===============================================================================
module UIActionHandlers
@@handlers = {}
module_function
def add(menu, action, hash)
@@handlers[menu] = HandlerHash.new if !@@handlers.has_key?(menu)
@@handlers[menu].add(action, hash)
end
def remove(menu, action)
@@handlers[menu]&.remove(action)
end
def clear(menu)
@@handlers[menu]&.clear
end
def get(menu, action)
return @@handlers[menu][action]
end
def each(menu)
return if !@@handlers.has_key?(menu)
@@handlers[menu].each { |action, hash| yield action, hash }
end
end

View File

@@ -1,172 +0,0 @@
#===============================================================================
# This module stores events that can happen during the game. A procedure can
# subscribe to an event by adding itself to the event. It will then be called
# whenever the event occurs.
#===============================================================================
module Events
@@OnMapCreate = Event.new
@@OnMapUpdate = Event.new
@@OnMapChange = Event.new
@@OnMapChanging = Event.new
@@OnMapSceneChange = Event.new
@@OnSpritesetCreate = Event.new
@@OnAction = Event.new
@@OnStepTaken = Event.new
@@OnLeaveTile = Event.new
@@OnStepTakenFieldMovement = Event.new
@@OnStepTakenTransferPossible = Event.new
@@OnStartBattle = Event.new
@@OnEndBattle = Event.new
@@OnWildPokemonCreate = Event.new
@@OnWildBattleOverride = Event.new
@@OnWildBattleEnd = Event.new
@@OnTrainerPartyLoad = Event.new
@@OnChangeDirection = Event.new
# Fires whenever a map is created. Event handler receives two parameters: the
# map (RPG::Map) and the tileset (RPG::Tileset)
def self.onMapCreate; @@OnMapCreate; end
def self.onMapCreate=(v); @@OnMapCreate = v; end
# Fires each frame during a map update.
def self.onMapUpdate; @@OnMapUpdate; end
def self.onMapUpdate=(v); @@OnMapUpdate = v; end
# Fires whenever one map is about to change to a different one. Event handler
# receives the new map ID and the Game_Map object representing the new map.
# When the event handler is called, $game_map still refers to the old map.
def self.onMapChanging; @@OnMapChanging; end
def self.onMapChanging=(v); @@OnMapChanging = v; end
# Fires whenever the player moves to a new map. Event handler receives the old
# map ID or 0 if none. Also fires when the first map of the game is loaded
def self.onMapChange; @@OnMapChange; end
def self.onMapChange=(v); @@OnMapChange = v; end
# Fires whenever the map scene is regenerated and soon after the player moves
# to a new map.
# Parameters:
# e[0] - Scene_Map object.
# e[1] - Whether the player just moved to a new map (either true or false). If
# false, some other code had called $scene.createSpritesets to
# regenerate the map scene without transferring the player elsewhere
def self.onMapSceneChange; @@OnMapSceneChange; end
def self.onMapSceneChange=(v); @@OnMapSceneChange = v; end
# Fires whenever a spriteset is created.
# Parameters:
# e[0] - Spriteset being created. e[0].map is the map associated with the
# spriteset (not necessarily the current map).
# e[1] - Viewport used for tilemap and characters
def self.onSpritesetCreate; @@OnSpritesetCreate; end
def self.onSpritesetCreate=(v); @@OnSpritesetCreate = v; end
# Triggers when the player presses the Action button on the map.
def self.onAction; @@OnAction; end
def self.onAction=(v); @@OnAction = v; end
# Fires whenever the player takes a step.
def self.onStepTaken; @@OnStepTaken; end
def self.onStepTaken=(v); @@OnStepTaken = v; end
# Fires whenever the player or another event leaves a tile.
# Parameters:
# e[0] - Event that just left the tile.
# e[1] - Map ID where the tile is located (not necessarily
# the current map). Use "$MapFactory.getMap(e[1])" to
# get the Game_Map object corresponding to that map.
# e[2] - X-coordinate of the tile
# e[3] - Y-coordinate of the tile
def self.onLeaveTile; @@OnLeaveTile; end
def self.onLeaveTile=(v); @@OnLeaveTile = v; end
# Fires whenever the player or another event enters a tile.
# Parameters:
# e[0] - Event that just entered a tile.
def self.onStepTakenFieldMovement; @@OnStepTakenFieldMovement; end
def self.onStepTakenFieldMovement=(v); @@OnStepTakenFieldMovement = v; end
# Fires whenever the player takes a step. The event handler may possibly move
# the player elsewhere.
# Parameters:
# e[0] - Array that contains a single boolean value. If an event handler moves
# the player to a new map, it should set this value to true. Other
# event handlers should check this parameter's value.
def self.onStepTakenTransferPossible; @@OnStepTakenTransferPossible; end
def self.onStepTakenTransferPossible=(v); @@OnStepTakenTransferPossible = v; end
def self.onStartBattle; @@OnStartBattle; end
def self.onStartBattle=(v); @@OnStartBattle = v; end
def self.onEndBattle; @@OnEndBattle; end
def self.onEndBattle=(v); @@OnEndBattle = v; end
# Triggers whenever a wild Pokémon is created
# Parameters:
# e[0] - Pokémon being created
def self.onWildPokemonCreate; @@OnWildPokemonCreate; end
def self.onWildPokemonCreate=(v); @@OnWildPokemonCreate = v; end
# Triggers at the start of a wild battle. Event handlers can provide their
# own wild battle routines to override the default behavior.
def self.onWildBattleOverride; @@OnWildBattleOverride; end
def self.onWildBattleOverride=(v); @@OnWildBattleOverride = v; end
# Triggers whenever a wild Pokémon battle ends
# Parameters:
# e[0] - Pokémon species
# e[1] - Pokémon level
# e[2] - Battle result (1-win, 2-loss, 3-escaped, 4-caught, 5-draw)
def self.onWildBattleEnd; @@OnWildBattleEnd; end
def self.onWildBattleEnd=(v); @@OnWildBattleEnd = v; end
# Triggers whenever an NPC trainer's Pokémon party is loaded
# Parameters:
# e[0] - Trainer
# e[1] - Items possessed by the trainer
# e[2] - Party
def self.onTrainerPartyLoad; @@OnTrainerPartyLoad; end
def self.onTrainerPartyLoad=(v); @@OnTrainerPartyLoad = v; end
# Fires whenever the player changes direction.
def self.onChangeDirection; @@OnChangeDirection; end
def self.onChangeDirection=(v); @@OnChangeDirection = v; end
end
#===============================================================================
#
#===============================================================================
def pbOnSpritesetCreate(spriteset,viewport)
Events.onSpritesetCreate.trigger(nil,spriteset,viewport)
end
#===============================================================================
# This module stores encounter-modifying events that can happen during the game.
# A procedure can subscribe to an event by adding itself to the event. It will
# then be called whenever the event occurs.
#===============================================================================
module EncounterModifier
@@procs = []
@@procsEnd = []
def self.register(p)
@@procs.push(p)
end
def self.registerEncounterEnd(p)
@@procsEnd.push(p)
end
def self.trigger(encounter)
for prc in @@procs
encounter = prc.call(encounter)
end
return encounter
end
def self.triggerEncounterEnd()
for prc in @@procsEnd
prc.call()
end
end
end

View File

@@ -0,0 +1,62 @@
#===============================================================================
# NOTE: Some Settings in here will be moved elsewhere eventually. They're all
# just gathered here while the new UI is being written.
#===============================================================================
module Settings
# :one, :adventure, :multiple
SAVE_SLOTS = :multiple
# Whether the main color of a move's name in the Fight menu in battle matches
# the pixel at coordinate (10,34) in cursor_fight.png for that move's type
# (true), or whether the move name's color is the default black (false).
BATTLE_MOVE_NAME_COLOR_FROM_GRAPHIC = true
# Whether "Town Map" will show as an option in the pause menu if the player
# has that item in the Bag and doesn't have a Pokégear.
SHOW_TOWN_MAP_IN_PAUSE_MENU = true
# The filename of a location sign graphic to be used if the map metadata for a
# map doesn't define one. Make this nil to use the default menu windowskin.
DEFAULT_LOCATION_SIGN_GRAPHIC = "Pt default"
# Assigns location sign graphics to text styles (numbers). These are used in
# class LocationWindow to display the text appropriately for the graphic being
# used. Style :none is reserved for the "no graphic" style. A filename may
# instead be an array of [filename, text base color, text shadow color].
LOCATION_SIGN_GRAPHIC_STYLES = {
:dp => [["DP", Color.new(72, 80, 72), Color.new(144, 160, 160)]],
:hgss => [["HGSS cave", Color.new(232, 232, 232), Color.new(120, 144, 160)],
["HGSS city", Color.new(56, 64, 72), Color.new(152, 152, 144)],
["HGSS default", Color.new(48, 64, 72), Color.new(144, 144, 96)],
["HGSS forest", Color.new(232, 232, 232), Color.new(120, 176, 144)],
["HGSS lake", Color.new(40, 48, 56), Color.new(104, 144, 192)],
["HGSS park", Color.new(40, 48, 56), Color.new(120, 136, 152)],
["HGSS route", Color.new(48, 64, 72), Color.new(136, 136, 104)],
["HGSS sea", Color.new(216, 240, 248), Color.new(24, 96, 144)],
["HGSS town", Color.new(48, 56, 64), Color.new(144, 120, 80)]],
:platinum => ["Pt cave", "Pt city", "Pt default", "Pt forest", "Pt lake",
"Pt park", "Pt route", "Pt sea", "Pt town"]
}
# Whether a move's power/type/category/etc. as shown in battle, the summary
# screen and the Move Reminder screen will appear as their calculated values
# (true) or their values from the PBS file moves.txt (false). For example, if
# this is true, Judgment's displayed type will depend on the Plate being held
# by the Pokémon that knows it.
SHOW_MODIFIED_MOVE_PROPERTIES = false
# Whether pressing Use in the Town Map will zoom it in to 200% and show a text
# pane on the right showing the selected point's description. The cursor can
# still be moved while zoomed in.
ENABLE_TOWN_MAP_ZOOM_IN_FOR_DETAILS = true
# Whether points in the Town Map can be marked.
ENABLE_TOWN_MAP_MARKING = true
# TODO: Allow renaming a Pokémon from the party screen/summary screen (not
# sure which). Gen 9 feature.
# TODO: Allow forgetting/remembering moves from the summary screen. Gen 9
# feature.
# TODO: Show usability party balls in the Bag. Maybe?
# TODO: Replace Run with Call in battle; don't have this depend on the Shadow
# type existing?
# TODO: Whether new items go at the top or bottom of its Bag pocket?
end

View File

@@ -4,11 +4,7 @@
# This class handles screen maintenance data, such as change in color tone,
# flashing, etc. Refer to "$game_screen" for the instance of this class.
#===============================================================================
class Game_Screen
#-----------------------------------------------------------------------------
# * Public Instance Variables
#-----------------------------------------------------------------------------
attr_reader :brightness # brightness
attr_reader :tone # color tone
attr_reader :flash_color # flash color
@@ -17,120 +13,106 @@ class Game_Screen
attr_reader :weather_type # weather type
attr_reader :weather_max # max number of weather sprites
attr_accessor :weather_duration # ticks in which the weather should fade in
#-----------------------------------------------------------------------------
# * Object Initialization
#-----------------------------------------------------------------------------
def initialize
@brightness = 255
@fadeout_duration = 0
@fadein_duration = 0
@tone = Tone.new(0, 0, 0, 0)
@tone_target = Tone.new(0, 0, 0, 0)
@tone_duration = 0
@flash_color = Color.new(0, 0, 0, 0)
@flash_duration = 0
@shake_power = 0
@shake_speed = 0
@shake_duration = 0
@shake_direction = 1
@shake = 0
@pictures = [nil]
for i in 1..100
@pictures.push(Game_Picture.new(i))
end
@weather_type = 0
@weather_max = 0.0
@weather_duration = 0
@brightness = 255
@tone = Tone.new(0, 0, 0, 0)
@tone_target = Tone.new(0, 0, 0, 0)
@tone_duration = 0
@tone_timer_start = nil
@flash_color = Color.new(0, 0, 0, 0)
@flash_duration = 0
@flash_timer_start = nil
@shake_power = 0
@shake_speed = 0
@shake_duration = 0
@shake = 0
@pictures = [nil]
(1..100).each { |i| @pictures.push(Game_Picture.new(i)) }
@weather_type = :None
@weather_max = 0.0
@weather_duration = 0
end
#-----------------------------------------------------------------------------
# * Start Changing Color Tone
# tone : color tone
# duration : time
#-----------------------------------------------------------------------------
# duration is time in 1/20ths of a second.
def start_tone_change(tone, duration)
@tone_target = tone.clone
@tone_duration = duration
if @tone_duration == 0
@tone = @tone_target.clone
if duration == 0
@tone = tone.clone
return
end
@tone_initial = @tone.clone
@tone_target = tone.clone
@tone_duration = duration / 20.0
@tone_timer_start = $stats.play_time
end
#-----------------------------------------------------------------------------
# * Start Flashing
# color : color
# duration : time
#-----------------------------------------------------------------------------
# duration is time in 1/20ths of a second.
def start_flash(color, duration)
@flash_color = color.clone
@flash_duration = duration
@flash_color = color.clone
@flash_initial_alpha = @flash_color.alpha
@flash_duration = duration / 20.0
@flash_timer_start = $stats.play_time
end
#-----------------------------------------------------------------------------
# * Start Shaking
# power : strength
# speed : speed
# duration : time
#-----------------------------------------------------------------------------
# duration is time in 1/20ths of a second.
def start_shake(power, speed, duration)
@shake_power = power
@shake_speed = speed
@shake_duration = duration
@shake_power = power
@shake_speed = speed
@shake_duration = duration / 20.0
@shake_timer_start = $stats.play_time
end
#-----------------------------------------------------------------------------
# * Set Weather
# type : type
# power : strength
# duration : time
#-----------------------------------------------------------------------------
# duration is time in 1/20ths of a second.
def weather(type, power, duration)
@weather_type = GameData::Weather.get(type).id
@weather_max = (power + 1) * RPG::Weather::MAX_SPRITES / 10
@weather_duration = duration # In 1/20ths of a seconds
@weather_duration = duration
end
#-----------------------------------------------------------------------------
# * Frame Update
#-----------------------------------------------------------------------------
def update
if @fadeout_duration && @fadeout_duration>=1
d = @fadeout_duration
@brightness = (@brightness*(d-1))/d
@fadeout_duration -= 1
end
if @fadein_duration && @fadein_duration>=1
d = @fadein_duration
@brightness = (@brightness*(d-1)+255)/d
@fadein_duration -= 1
end
if @tone_duration>=1
d = @tone_duration
@tone.red = (@tone.red*(d-1)+@tone_target.red)/d
@tone.green = (@tone.green*(d-1)+@tone_target.green)/d
@tone.blue = (@tone.blue*(d-1)+@tone_target.blue)/d
@tone.gray = (@tone.gray*(d-1)+@tone_target.gray)/d
@tone_duration -= 1
end
if @flash_duration>=1
d = @flash_duration
@flash_color.alpha = @flash_color.alpha*(d-1)/d
@flash_duration -= 1
end
if @shake_duration>=1 || @shake!=0
delta = (@shake_power*@shake_speed*@shake_direction)/10.0
if @shake_duration<=1 && @shake*(@shake+delta)<0
@shake = 0
else
@shake += delta
now = $stats.play_time
if @tone_timer_start
@tone.red = lerp(@tone_initial.red, @tone_target.red, @tone_duration, @tone_timer_start, now)
@tone.green = lerp(@tone_initial.green, @tone_target.green, @tone_duration, @tone_timer_start, now)
@tone.blue = lerp(@tone_initial.blue, @tone_target.blue, @tone_duration, @tone_timer_start, now)
@tone.gray = lerp(@tone_initial.gray, @tone_target.gray, @tone_duration, @tone_timer_start, now)
if now - @tone_timer_start >= @tone_duration
@tone_initial = nil
@tone_timer_start = nil
end
end
if @flash_timer_start
@flash_color.alpha = lerp(@flash_initial_alpha, 0, @flash_duration, @flash_timer_start, now)
if now - @flash_timer_start >= @flash_duration
@flash_initial_alpha = nil
@flash_timer_start = nil
end
end
if @shake_timer_start
delta_t = now - @shake_timer_start
movement_per_second = @shake_power * @shake_speed * 4
limit = @shake_power * 2.5 # Maximum pixel displacement
phase = (delta_t * movement_per_second / limit).to_i % 4
case phase
when 0, 2
@shake = (movement_per_second * delta_t) % limit
@shake *= -1 if phase == 2
else
@shake = limit - ((movement_per_second * delta_t) % limit)
@shake *= -1 if phase == 3
end
if delta_t >= @shake_duration
@shake_phase = phase if !@shake_phase || phase == 1 || phase == 3
if phase != @shake_phase || @shake < 2
@shake_timer_start = nil
@shake = 0
end
end
@shake_direction = -1 if @shake>@shake_power*2
@shake_direction = 1 if @shake<-@shake_power*2
@shake_duration -= 1 if @shake_duration>=1
end
if $game_temp.in_battle
for i in 51..100
@pictures[i].update
end
(51..100).each { |i| @pictures[i].update }
else
for i in 1..50
@pictures[i].update
end
(1..50).each { |i| @pictures[i].update }
end
end
end
@@ -138,17 +120,15 @@ end
#===============================================================================
#
#===============================================================================
def pbToneChangeAll(tone,duration)
$game_screen.start_tone_change(tone,duration*Graphics.frame_rate/20)
for picture in $game_screen.pictures
picture.start_tone_change(tone,duration*Graphics.frame_rate/20) if picture
end
def pbToneChangeAll(tone, duration)
$game_screen.start_tone_change(tone, duration)
$game_screen.pictures.each { |picture| picture&.start_tone_change(tone, duration) }
end
def pbShake(power,speed,frames)
$game_screen.start_shake(power,speed,frames*Graphics.frame_rate/20)
def pbFlash(color, frames)
$game_screen.start_flash(color, frames)
end
def pbFlash(color,frames)
$game_screen.start_flash(color,frames*Graphics.frame_rate/20)
def pbShake(power, speed, frames)
$game_screen.start_shake(power, speed, frames)
end

View File

@@ -5,51 +5,83 @@
# Refer to "$game_temp" for the instance of this class.
#===============================================================================
class Game_Temp
attr_accessor :message_window_showing # message window showing
attr_accessor :common_event_id # common event ID
attr_accessor :in_battle # in-battle flag
attr_accessor :battle_abort # battle flag: interrupt
attr_accessor :battleback_name # battleback file name
attr_accessor :in_menu # menu is open
attr_accessor :menu_beep # menu: play sound effect flag
# Flags requesting something to happen
attr_accessor :menu_calling # menu calling flag
attr_accessor :ready_menu_calling # ready menu calling flag
attr_accessor :debug_calling # debug calling flag
attr_accessor :interact_calling # EventHandlers.trigger(:on_player_interact) flag
attr_accessor :battle_abort # battle flag: interrupt (unused)
attr_accessor :title_screen_calling # return to title screen flag
attr_accessor :common_event_id # common event ID to start
attr_accessor :field_move_to_use
attr_accessor :field_move_user
# Flags indicating something is happening
attr_accessor :in_menu # menu is open
attr_accessor :in_storage # in-Pokémon storage flag
attr_accessor :in_battle # in-battle flag
attr_accessor :message_window_showing # message window showing
attr_accessor :ending_surf # jumping off surf base flag
attr_accessor :surf_base_coords # [x, y] while jumping on/off, or nil
attr_accessor :in_mini_update # performing mini update flag
# Battle
attr_accessor :battleback_name # battleback file name
attr_accessor :force_single_battle # force next battle to be 1v1 flag
attr_accessor :waiting_trainer # [trainer, event ID] or nil
attr_accessor :last_battle_record # record of actions in last recorded battle
# Overrides transfers
attr_accessor :player_transferring # player place movement flag
attr_accessor :player_new_map_id # player destination: map ID
attr_accessor :player_new_x # player destination: x-coordinate
attr_accessor :player_new_y # player destination: y-coordinate
attr_accessor :player_new_direction # player destination: direction
attr_accessor :fly_destination # [map ID, x, y] or nil
# Transitions
attr_accessor :transition_processing # transition processing flag
attr_accessor :transition_name # transition file name
attr_accessor :to_title # return to title screen flag
attr_accessor :fadestate # for sprite hashes
attr_accessor :background_bitmap
attr_accessor :fadestate # for sprite hashes
# Other
attr_accessor :menu_beep # menu: play sound effect flag
attr_accessor :menu_last_choice # pause menu: index of last selection
attr_accessor :memorized_bgm # set when trainer intro BGM is played
attr_accessor :memorized_bgm_position # set when trainer intro BGM is played
attr_accessor :darkness_sprite # DarknessSprite or nil
attr_accessor :mart_prices
#-----------------------------------------------------------------------------
# * Object Initialization
#-----------------------------------------------------------------------------
def initialize
@message_window_showing = false
@common_event_id = 0
@in_battle = false
@battle_abort = false
@battleback_name = ''
@in_menu = false
@menu_beep = false
# Flags requesting something to happen
@menu_calling = false
@ready_menu_calling = false
@debug_calling = false
@interact_calling = false
@battle_abort = false
@title_screen_calling = false
@common_event_id = 0
# Flags indicating something is happening
@in_menu = false
@in_storage = false
@in_battle = false
@message_window_showing = false
@ending_surf = false
@in_mini_update = false
# Battle
@battleback_name = ""
@force_single_battle = false
# Overrides transfers
@player_transferring = false
@player_new_map_id = 0
@player_new_x = 0
@player_new_y = 0
@player_new_direction = 0
# Transitions
@transition_processing = false
@transition_name = ""
@to_title = false
@fadestate = 0
@background_bitmap = nil
@message_window_showing = false
@transition_processing = false
# Other
@menu_beep = false
@memorized_bgm = nil
@memorized_bgm_position = 0
@menu_last_choice = 0
@mart_prices = {}
end

View File

@@ -5,25 +5,20 @@
# Refer to "$game_switches" for the instance of this class.
#===============================================================================
class Game_Switches
#-----------------------------------------------------------------------------
# * Object Initialization
#-----------------------------------------------------------------------------
def initialize
@data = []
end
#-----------------------------------------------------------------------------
# * Get Switch
# Get Switch
# switch_id : switch ID
#-----------------------------------------------------------------------------
def [](switch_id)
return @data[switch_id] if switch_id <= 5000 && @data[switch_id] != nil
return @data[switch_id] if switch_id <= 5000 && @data[switch_id]
return false
end
#-----------------------------------------------------------------------------
# * Set Switch
# Set Switch
# switch_id : switch ID
# value : ON (true) / OFF (false)
#-----------------------------------------------------------------------------
def []=(switch_id, value)
@data[switch_id] = value if switch_id <= 5000
end

View File

@@ -5,25 +5,20 @@
# Refer to "$game_variables" for the instance of this class.
#===============================================================================
class Game_Variables
#-----------------------------------------------------------------------------
# * Object Initialization
#-----------------------------------------------------------------------------
def initialize
@data = []
end
#-----------------------------------------------------------------------------
# * Get Variable
# Get Variable
# variable_id : variable ID
#-----------------------------------------------------------------------------
def [](variable_id)
return @data[variable_id] if variable_id <= 5000 && !@data[variable_id].nil?
return 0
end
#-----------------------------------------------------------------------------
# * Set Variable
# Set Variable
# variable_id : variable ID
# value : the variable's value
#-----------------------------------------------------------------------------
def []=(variable_id, value)
@data[variable_id] = value if variable_id <= 5000
end

View File

@@ -5,24 +5,19 @@
# "Hash." Refer to "$game_self_switches" for the instance of this class.
#===============================================================================
class Game_SelfSwitches
#-----------------------------------------------------------------------------
# * Object Initialization
#-----------------------------------------------------------------------------
def initialize
@data = {}
end
#-----------------------------------------------------------------------------
# * Get Self Switch
# Get Self Switch
# key : key
#-----------------------------------------------------------------------------
def [](key)
return (@data[key]==true) ? true : false
return @data[key] == true
end
#-----------------------------------------------------------------------------
# * Set Self Switch
# Set Self Switch
# key : key
# value : ON (true) / OFF (false)
#-----------------------------------------------------------------------------
def []=(key, value)
@data[key] = value
end

View File

@@ -1,15 +1,15 @@
#==============================================================================
#===============================================================================
# ** Game_System
#------------------------------------------------------------------------------
# This class handles data surrounding the system. Backround music, etc.
# is managed here as well. Refer to "$game_system" for the instance of
# this class.
#==============================================================================
#===============================================================================
class Game_System
attr_reader :map_interpreter # map event interpreter
attr_reader :battle_interpreter # battle event interpreter
attr_accessor :timer # timer
attr_accessor :timer_working # timer working flag
attr_accessor :timer_start # $stats.play_time when timer was started, or nil
attr_accessor :timer_duration # Time (in seconds) the timer is initially set to
attr_accessor :save_disabled # save forbidden
attr_accessor :menu_disabled # menu forbidden
attr_accessor :encounter_disabled # encounter forbidden
@@ -22,66 +22,99 @@ class Game_System
attr_accessor :bgm_position
def initialize
@map_interpreter = Interpreter.new(0, true)
@battle_interpreter = Interpreter.new(0, false)
@timer = 0
@timer_working = false
@save_disabled = false
@menu_disabled = false
@encounter_disabled = false
@message_position = 2
@message_frame = 0
@save_count = 0
@magic_number = 0
@autoscroll_x_speed = 0
@autoscroll_y_speed = 0
@bgm_position = 0
@bgs_position = 0
@map_interpreter = Interpreter.new(0, true)
@battle_interpreter = Interpreter.new(0, false)
@timer_start = nil
@timer_duration = 0
@save_disabled = false
@menu_disabled = false
@encounter_disabled = false
@message_position = 2
@message_frame = 0
@save_count = 0
@magic_number = 0
@adventure_magic_number = rand(2**32)
@autoscroll_x_speed = 0
@autoscroll_y_speed = 0
@bgm_position = 0
@bgs_position = 0
end
################################################################################
def adventure_magic_number
@adventure_magic_number ||= rand(2**32)
return @adventure_magic_number
end
def bgm_play(bgm)
def battle_bgm
return (@battle_bgm) ? @battle_bgm : $data_system.battle_bgm
end
attr_writer :battle_bgm
def battle_end_me
return (@battle_end_me) ? @battle_end_me : $data_system.battle_end_me
end
attr_writer :battle_end_me
def windowskin_name
return $data_system.windowskin_name if @windowskin_name.nil?
return @windowskin_name
end
attr_writer :windowskin_name
def timer
return 0 if !@timer_start || !$stats
return @timer_duration - $stats.play_time + @timer_start
end
#-----------------------------------------------------------------------------
def bgm_play(bgm, track = nil)
old_pos = @bgm_position
@bgm_position = 0
bgm_play_internal(bgm,0)
bgm_play_internal(bgm, 0, track)
@bgm_position = old_pos
end
def bgm_play_internal2(name,volume,pitch,position) # :nodoc:
def bgm_play_internal2(name, volume, pitch, position, track = nil) # :nodoc:
vol = volume
vol *= $PokemonSystem.bgmvolume/100.0
vol *= $PokemonSystem.bgmvolume / 100.0
vol = vol.to_i
begin
Audio.bgm_play(name,vol,pitch,position)
Audio.bgm_play(name, vol, pitch, position, track)
rescue ArgumentError
Audio.bgm_play(name,vol,pitch)
Audio.bgm_play(name, vol, pitch, 0, track)
end
end
def bgm_play_internal(bgm,position) # :nodoc:
@bgm_position = position if !@bgm_paused
@playing_bgm = (bgm==nil) ? nil : bgm.clone
if bgm!=nil && bgm.name!=""
if FileTest.audio_exist?("Audio/BGM/"+bgm.name)
bgm_play_internal2("Audio/BGM/"+bgm.name,
bgm.volume,bgm.pitch,@bgm_position) if !@defaultBGM
def bgm_play_internal(bgm, position, track = nil) # :nodoc:
if !track || track == 0
@bgm_position = position if !@bgm_paused
@playing_bgm = bgm&.clone
end
if bgm && bgm.name != ""
if !@defaultBGM && FileTest.audio_exist?("Audio/BGM/" + bgm.name)
bgm_play_internal2("Audio/BGM/" + bgm.name, bgm.volume, bgm.pitch, @bgm_position, track)
end
else
@bgm_position = position if !@bgm_paused
@playing_bgm = nil
Audio.bgm_stop if !@defaultBGM
if !track || track == 0
@bgm_position = position if !@bgm_paused
@playing_bgm = nil
end
Audio.bgm_stop(track) if !@defaultBGM
end
if @defaultBGM
bgm_play_internal2("Audio/BGM/"+@defaultBGM.name,
@defaultBGM.volume,@defaultBGM.pitch,@bgm_position)
bgm_play_internal2("Audio/BGM/" + @defaultBGM.name,
@defaultBGM.volume, @defaultBGM.pitch, @bgm_position, track)
end
Graphics.frame_reset
end
def bgm_pause(fadetime=0.0) # :nodoc:
def bgm_pause(fadetime = 0.0) # :nodoc:
pos = Audio.bgm_pos rescue 0
self.bgm_fade(fadetime) if fadetime>0.0
self.bgm_fade(fadetime) if fadetime > 0.0
@bgm_position = pos
@bgm_paused = true
end
@@ -93,22 +126,26 @@ class Game_System
def bgm_resume(bgm) # :nodoc:
if @bgm_paused
self.bgm_play_internal(bgm,@bgm_position)
self.bgm_play_internal(bgm, @bgm_position)
@bgm_position = 0
@bgm_paused = false
end
end
def bgm_stop # :nodoc:
@bgm_position = 0 if !@bgm_paused
@playing_bgm = nil
Audio.bgm_stop if !@defaultBGM
def bgm_stop(track = nil) # :nodoc:
if !track || track == 0
@bgm_position = 0 if !@bgm_paused
@playing_bgm = nil
end
Audio.bgm_stop(track) if !@defaultBGM
end
def bgm_fade(time) # :nodoc:
@bgm_position = 0 if !@bgm_paused
@playing_bgm = nil
Audio.bgm_fade((time*1000).floor) if !@defaultBGM
def bgm_fade(time, track = nil) # :nodoc:
if !track || track == 0
@bgm_position = 0 if !@bgm_paused
@playing_bgm = nil
end
Audio.bgm_fade((time * 1000).floor, track) if !@defaultBGM
end
def playing_bgm
@@ -130,28 +167,27 @@ class Game_System
return (@playing_bgm) ? @playing_bgm.clone : nil
end
def setDefaultBGM(bgm,volume=80,pitch=100)
bgm = RPG::AudioFile.new(bgm,volume,pitch) if bgm.is_a?(String)
if bgm!=nil && bgm.name!=""
@defaultBGM = nil
def setDefaultBGM(bgm, volume = 80, pitch = 100)
bgm = RPG::AudioFile.new(bgm, volume, pitch) if bgm.is_a?(String)
@defaultBGM = nil
if bgm && bgm.name != ""
self.bgm_play(bgm)
@defaultBGM = bgm.clone
else
@defaultBGM = nil
self.bgm_play(@playing_bgm)
end
end
################################################################################
#-----------------------------------------------------------------------------
def me_play(me)
me = RPG::AudioFile.new(me) if me.is_a?(String)
if me!=nil && me.name!=""
if FileTest.audio_exist?("Audio/ME/"+me.name)
if me && me.name != ""
if FileTest.audio_exist?("Audio/ME/" + me.name)
vol = me.volume
vol *= $PokemonSystem.bgmvolume/100.0
vol *= $PokemonSystem.bgmvolume / 100.0
vol = vol.to_i
Audio.me_play("Audio/ME/"+me.name,vol,me.pitch)
Audio.me_play("Audio/ME/" + me.name, vol, me.pitch)
end
else
Audio.me_stop
@@ -159,16 +195,16 @@ class Game_System
Graphics.frame_reset
end
################################################################################
#-----------------------------------------------------------------------------
def bgs_play(bgs)
@playing_bgs = (bgs==nil) ? nil : bgs.clone
if bgs!=nil && bgs.name!=""
if FileTest.audio_exist?("Audio/BGS/"+bgs.name)
@playing_bgs = (bgs.nil?) ? nil : bgs.clone
if bgs && bgs.name != ""
if FileTest.audio_exist?("Audio/BGS/" + bgs.name)
vol = bgs.volume
vol *= $PokemonSystem.sevolume/100.0
vol *= $PokemonSystem.sevolume / 100.0
vol = vol.to_i
Audio.bgs_play("Audio/BGS/"+bgs.name,vol,bgs.pitch)
Audio.bgs_play("Audio/BGS/" + bgs.name, vol, bgs.pitch)
end
else
@bgs_position = 0
@@ -178,8 +214,8 @@ class Game_System
Graphics.frame_reset
end
def bgs_pause(fadetime=0.0) # :nodoc:
if fadetime>0.0
def bgs_pause(fadetime = 0.0) # :nodoc:
if fadetime > 0.0
self.bgs_fade(fadetime)
else
self.bgs_stop
@@ -207,7 +243,7 @@ class Game_System
def bgs_fade(time)
@bgs_position = 0
@playing_bgs = nil
Audio.bgs_fade((time*1000).floor)
Audio.bgs_fade((time * 1000).floor)
end
def playing_bgs
@@ -226,15 +262,15 @@ class Game_System
return (@playing_bgs) ? @playing_bgs.clone : nil
end
################################################################################
#-----------------------------------------------------------------------------
def se_play(se)
se = RPG::AudioFile.new(se) if se.is_a?(String)
if se!=nil && se.name!="" && FileTest.audio_exist?("Audio/SE/"+se.name)
if se && se.name != "" && FileTest.audio_exist?("Audio/SE/" + se.name)
vol = se.volume
vol *= $PokemonSystem.sevolume/100.0
vol *= $PokemonSystem.sevolume / 100.0
vol = vol.to_i
Audio.se_play("Audio/SE/"+se.name,vol,se.pitch)
Audio.se_play("Audio/SE/" + se.name, vol, se.pitch)
end
end
@@ -242,43 +278,12 @@ class Game_System
Audio.se_stop
end
################################################################################
def battle_bgm
return (@battle_bgm) ? @battle_bgm : $data_system.battle_bgm
end
def battle_bgm=(battle_bgm)
@battle_bgm = battle_bgm
end
def battle_end_me
return (@battle_end_me) ? @battle_end_me : $data_system.battle_end_me
end
def battle_end_me=(battle_end_me)
@battle_end_me = battle_end_me
end
################################################################################
def windowskin_name
if @windowskin_name==nil
return $data_system.windowskin_name
else
return @windowskin_name
end
end
def windowskin_name=(windowskin_name)
@windowskin_name = windowskin_name
end
#-----------------------------------------------------------------------------
def update
@timer -= 1 if @timer_working && @timer>0
if Input.trigger?(Input::SPECIAL) && pbCurrentEventCommentInput(1,"Cut Scene")
event = @map_interpreter.get_character(0)
@map_interpreter.pbSetSelfSwitch(event.id,"A",true)
if Input.trigger?(Input::SPECIAL) && pbCurrentEventCommentInput(1, "Cut Scene")
event = @map_interpreter.get_self
@map_interpreter.pbSetSelfSwitch(event.id, "A", true)
@map_interpreter.command_end
event.start
end

View File

@@ -4,11 +4,7 @@
# This class handles the picture. It's used within the Game_Screen class
# ($game_screen).
#===============================================================================
class Game_Picture
#-----------------------------------------------------------------------------
# * Public Instance Variables
#-----------------------------------------------------------------------------
attr_reader :number # picture number
attr_reader :name # file name
attr_reader :origin # starting point
@@ -20,10 +16,7 @@ class Game_Picture
attr_reader :blend_type # blend method
attr_reader :tone # color tone
attr_reader :angle # rotation angle
#-----------------------------------------------------------------------------
# * Object Initialization
# number : picture number
#-----------------------------------------------------------------------------
def initialize(number)
@number = number
@name = ""
@@ -35,6 +28,7 @@ class Game_Picture
@opacity = 255.0
@blend_type = 1
@duration = 0
@move_timer_start = nil
@target_x = @x
@target_y = @y
@target_zoom_x = @zoom_x
@@ -43,11 +37,12 @@ class Game_Picture
@tone = Tone.new(0, 0, 0, 0)
@tone_target = Tone.new(0, 0, 0, 0)
@tone_duration = 0
@tone_timer_start = nil
@angle = 0
@rotate_speed = 0
end
#-----------------------------------------------------------------------------
# * Show Picture
# Show Picture
# name : file name
# origin : starting point
# x : x-coordinate
@@ -56,7 +51,6 @@ class Game_Picture
# zoom_y : y directional zoom rate
# opacity : opacity level
# blend_type : blend method
#-----------------------------------------------------------------------------
def show(name, origin, x, y, zoom_x, zoom_y, opacity, blend_type)
@name = name
@origin = origin
@@ -65,7 +59,7 @@ class Game_Picture
@zoom_x = zoom_x.to_f
@zoom_y = zoom_y.to_f
@opacity = opacity.to_f
@blend_type = blend_type ? blend_type : 0
@blend_type = blend_type || 0
@duration = 0
@target_x = @x
@target_y = @y
@@ -75,12 +69,13 @@ class Game_Picture
@tone = Tone.new(0, 0, 0, 0)
@tone_target = Tone.new(0, 0, 0, 0)
@tone_duration = 0
@tone_timer_start = nil
@angle = 0
@rotate_speed = 0
end
#-----------------------------------------------------------------------------
# * Move Picture
# duration : time
# Move Picture
# duration : time in 1/20ths of a second
# origin : starting point
# x : x-coordinate
# y : y-coordinate
@@ -88,65 +83,80 @@ class Game_Picture
# zoom_y : y directional zoom rate
# opacity : opacity level
# blend_type : blend method
#-----------------------------------------------------------------------------
def move(duration, origin, x, y, zoom_x, zoom_y, opacity, blend_type)
@duration = duration
@origin = origin
@target_x = x.to_f
@target_y = y.to_f
@target_zoom_x = zoom_x.to_f
@target_zoom_y = zoom_y.to_f
@target_opacity = opacity.to_f
@blend_type = blend_type ? blend_type : 0
@duration = duration / 20.0
@origin = origin
@initial_x = @x
@initial_y = @y
@target_x = x.to_f
@target_y = y.to_f
@initial_zoom_x = @zoom_x
@initial_zoom_y = @zoom_y
@target_zoom_x = zoom_x.to_f
@target_zoom_y = zoom_y.to_f
@initial_opacity = @opacity
@target_opacity = opacity.to_f
@blend_type = blend_type || 0
@move_timer_start = $stats.play_time
end
#-----------------------------------------------------------------------------
# * Change Rotation Speed
# speed : rotation speed
#-----------------------------------------------------------------------------
# Change Rotation Speed
# speed : rotation speed (degrees to change per 1/20th of a second)
def rotate(speed)
@rotate_timer = (speed == 0) ? nil : System.uptime # Time since last frame
@rotate_speed = speed
end
#-----------------------------------------------------------------------------
# * Start Change of Color Tone
# Start Change of Color Tone
# tone : color tone
# duration : time
#-----------------------------------------------------------------------------
# duration : time in 1/20ths of a second
def start_tone_change(tone, duration)
@tone_target = tone.clone
@tone_duration = duration
if @tone_duration == 0
@tone = @tone_target.clone
if duration == 0
@tone = tone.clone
return
end
@tone_initial = @tone.clone
@tone_target = tone.clone
@tone_duration = duration / 20.0
@tone_timer_start = $stats.play_time
end
#-----------------------------------------------------------------------------
# * Erase Picture
#-----------------------------------------------------------------------------
def erase
@name = ""
end
#-----------------------------------------------------------------------------
# * Frame Update
#-----------------------------------------------------------------------------
def update
if @duration >= 1
d = @duration
@x = (@x * (d - 1) + @target_x) / d
@y = (@y * (d - 1) + @target_y) / d
@zoom_x = (@zoom_x * (d - 1) + @target_zoom_x) / d
@zoom_y = (@zoom_y * (d - 1) + @target_zoom_y) / d
@opacity = (@opacity * (d - 1) + @target_opacity) / d
@duration -= 1
return if @name == ""
now = $stats.play_time
if @move_timer_start
@x = lerp(@initial_x, @target_x, @duration, @move_timer_start, now)
@y = lerp(@initial_y, @target_y, @duration, @move_timer_start, now)
@zoom_x = lerp(@initial_zoom_x, @target_zoom_x, @duration, @move_timer_start, now)
@zoom_y = lerp(@initial_zoom_y, @target_zoom_y, @duration, @move_timer_start, now)
@opacity = lerp(@initial_opacity, @target_opacity, @duration, @move_timer_start, now)
if now - @move_timer_start >= @duration
@initial_x = nil
@initial_y = nil
@initial_zoom_x = nil
@initial_zoom_y = nil
@initial_opacity = nil
@move_timer_start = nil
end
end
if @tone_duration >= 1
d = @tone_duration
@tone.red = (@tone.red * (d - 1) + @tone_target.red) / d
@tone.green = (@tone.green * (d - 1) + @tone_target.green) / d
@tone.blue = (@tone.blue * (d - 1) + @tone_target.blue) / d
@tone.gray = (@tone.gray * (d - 1) + @tone_target.gray) / d
@tone_duration -= 1
if @tone_timer_start
@tone.red = lerp(@tone_initial.red, @tone_target.red, @tone_duration, @tone_timer_start, now)
@tone.green = lerp(@tone_initial.green, @tone_target.green, @tone_duration, @tone_timer_start, now)
@tone.blue = lerp(@tone_initial.blue, @tone_target.blue, @tone_duration, @tone_timer_start, now)
@tone.gray = lerp(@tone_initial.gray, @tone_target.gray, @tone_duration, @tone_timer_start, now)
if now - @tone_timer_start >= @tone_duration
@tone_initial = nil
@tone_timer_start = nil
end
end
if @rotate_speed != 0
@angle += @rotate_speed / 2.0
@rotate_timer = System.uptime if !@rotate_timer
@angle += @rotate_speed * (System.uptime - @rotate_timer) * 20.0
@rotate_timer = System.uptime
while @angle < 0
@angle += 360
end

View File

@@ -1,9 +1,9 @@
#==============================================================================
#===============================================================================
# ** Game_Map
#------------------------------------------------------------------------------
# This class handles the map. It includes scrolling and passable determining
# functions. Refer to "$game_map" for the instance of this class.
#==============================================================================
#===============================================================================
class Game_Map
attr_accessor :map_id
attr_accessor :tileset_name # tileset file name
@@ -43,32 +43,34 @@ class Game_Map
end
def setup(map_id)
@map_id = map_id
@map = load_data(sprintf("Data/Map%03d.rxdata",map_id))
@map_id = map_id
@map = load_data(sprintf("Data/Map%03d.rxdata", map_id))
tileset = $data_tilesets[@map.tileset_id]
updateTileset
@fog_ox = 0
@fog_oy = 0
@fog_tone = Tone.new(0, 0, 0, 0)
@fog_tone_target = Tone.new(0, 0, 0, 0)
@fog_tone_duration = 0
@fog_opacity_duration = 0
@fog_opacity_target = 0
self.display_x = 0
self.display_y = 0
@need_refresh = false
Events.onMapCreate.trigger(self,map_id,@map,tileset)
@events = {}
for i in @map.events.keys
@events[i] = Game_Event.new(@map_id, @map.events[i],self)
@fog_ox = 0
@fog_oy = 0
@fog_tone = Tone.new(0, 0, 0, 0)
@fog_tone_target = Tone.new(0, 0, 0, 0)
@fog_tone_duration = 0
@fog_tone_timer_start = nil
@fog_opacity_duration = 0
@fog_opacity_target = 0
@fog_opacity_timer_start = nil
self.display_x = 0
self.display_y = 0
@need_refresh = false
EventHandlers.trigger(:on_game_map_setup, map_id, @map, tileset)
@events = {}
@map.events.each_key do |i|
@events[i] = Game_Event.new(@map_id, @map.events[i], self)
end
@common_events = {}
for i in 1...$data_common_events.size
@common_events[i] = Game_CommonEvent.new(i)
@common_events = {}
(1...$data_common_events.size).each do |i|
@common_events[i] = Game_CommonEvent.new(i)
end
@scroll_direction = 2
@scroll_rest = 0
@scroll_speed = 4
@scroll_distance_x = 0
@scroll_distance_y = 0
@scroll_speed = 4
end
def updateTileset
@@ -95,57 +97,52 @@ class Game_Map
def encounter_list; return @map.encounter_list; end
def encounter_step; return @map.encounter_step; end
def data; return @map.data; end
def tileset_id; return @map.tileset_id; end
def bgm; return @map.bgm; end
def name
ret = pbGetMessage(MessageTypes::MapNames,@map_id)
ret.gsub!(/\\PN/,$Trainer.name) if $Trainer
return ret
return pbGetMapNameFromId(@map_id)
end
#-----------------------------------------------------------------------------
# * Autoplays background music
# Plays music called "[normal BGM]_n" if it's night time and it exists
#-----------------------------------------------------------------------------
def metadata
return GameData::MapMetadata.try_get(@map_id)
end
# Returns the name of this map's BGM. If it's night time, returns the night
# version of the BGM (if it exists).
def bgm_name
if PBDayNight.isNight? && FileTest.audio_exist?("Audio/BGM/" + @map.bgm.name + "_n")
return @map.bgm.name + "_n"
end
return @map.bgm.name
end
# Autoplays background music
# Plays music called "[normal BGM]_n" if it's night time and it exists
def autoplayAsCue
if @map.autoplay_bgm
if PBDayNight.isNight? && FileTest.audio_exist?("Audio/BGM/"+ @map.bgm.name+ "_n")
pbCueBGM(@map.bgm.name+"_n",1.0,@map.bgm.volume,@map.bgm.pitch)
else
pbCueBGM(@map.bgm,1.0)
end
end
if @map.autoplay_bgs
pbBGSPlay(@map.bgs)
end
pbCueBGM(bgm_name, 1.0, @map.bgm.volume, @map.bgm.pitch) if @map.autoplay_bgm
pbBGSPlay(@map.bgs) if @map.autoplay_bgs
end
#-----------------------------------------------------------------------------
# * Plays background music
# Plays music called "[normal BGM]_n" if it's night time and it exists
#-----------------------------------------------------------------------------
# Plays background music
# Plays music called "[normal BGM]_n" if it's night time and it exists
def autoplay
if @map.autoplay_bgm
if PBDayNight.isNight? && FileTest.audio_exist?("Audio/BGM/"+ @map.bgm.name+ "_n")
pbBGMPlay(@map.bgm.name+"_n",@map.bgm.volume,@map.bgm.pitch)
else
pbBGMPlay(@map.bgm)
end
end
if @map.autoplay_bgs
pbBGSPlay(@map.bgs)
end
pbBGMPlay(bgm_name, @map.bgm.volume, @map.bgm.pitch) if @map.autoplay_bgm
pbBGSPlay(@map.bgs) if @map.autoplay_bgs
end
def valid?(x, y)
return x>=0 && x<width && y>=0 && y<height
return x >= 0 && x < width && y >= 0 && y < height
end
def validLax?(x, y)
return x>=-10 && x<=width+10 && y>=-10 && y<=height+10
return x >= -10 && x <= width + 10 && y >= -10 && y <= height + 10
end
def passable?(x, y, d, self_event = nil)
def passable?(x, y, dir, self_event = nil)
return false if !valid?(x, y)
bit = (1 << (d / 2 - 1)) & 0x0f
for event in events.values
bit = (1 << ((dir / 2) - 1)) & 0x0f
events.each_value do |event|
next if event.tile_id <= 0
next if event == self_event
next if !event.at_coordinate?(x, y)
@@ -156,11 +153,11 @@ class Game_Map
return false if passage & 0x0f == 0x0f
return true if @priorities[event.tile_id] == 0
end
return playerPassable?(x, y, d, self_event) if self_event==$game_player
return playerPassable?(x, y, dir, self_event) if self_event == $game_player
# All other events
newx = x
newy = y
case d
case dir
when 1
newx -= 1
newy += 1
@@ -183,14 +180,15 @@ class Game_Map
newy -= 1
end
return false if !valid?(newx, newy)
for i in [2, 1, 0]
[2, 1, 0].each do |i|
tile_id = data[x, y, i]
terrain = GameData::TerrainTag.try_get(@terrain_tags[tile_id])
# If already on water, only allow movement to another water tile
if self_event != nil && terrain.can_surf_freely
for j in [2, 1, 0]
if self_event && terrain.can_surf_freely
[2, 1, 0].each do |j|
facing_tile_id = data[newx, newy, j]
return false if facing_tile_id == nil
next if facing_tile_id == 0
return false if facing_tile_id.nil?
facing_terrain = GameData::TerrainTag.try_get(@terrain_tags[facing_tile_id])
if facing_terrain.id != :None && !facing_terrain.ignore_passability
return facing_terrain.can_surf_freely
@@ -200,30 +198,32 @@ class Game_Map
# Can't walk onto ice
elsif terrain.ice
return false
elsif self_event != nil && self_event.x == x && self_event.y == y
elsif self_event && self_event.x == x && self_event.y == y
# Can't walk onto ledges
for j in [2, 1, 0]
[2, 1, 0].each do |j|
facing_tile_id = data[newx, newy, j]
return false if facing_tile_id == nil
next if facing_tile_id == 0
return false if facing_tile_id.nil?
facing_terrain = GameData::TerrainTag.try_get(@terrain_tags[facing_tile_id])
return false if facing_terrain.ledge
break if facing_terrain.id != :None && !facing_terrain.ignore_passability
end
end
next if terrain&.ignore_passability
next if tile_id == 0
# Regular passability checks
if !terrain || !terrain.ignore_passability
passage = @passages[tile_id]
return false if passage & bit != 0 || passage & 0x0f == 0x0f
return true if @priorities[tile_id] == 0
end
passage = @passages[tile_id]
return false if passage & bit != 0 || passage & 0x0f == 0x0f
return true if @priorities[tile_id] == 0
end
return true
end
def playerPassable?(x, y, d, self_event = nil)
bit = (1 << (d / 2 - 1)) & 0x0f
for i in [2, 1, 0]
def playerPassable?(x, y, dir, self_event = nil)
bit = (1 << ((dir / 2) - 1)) & 0x0f
[2, 1, 0].each do |i|
tile_id = data[x, y, i]
next if tile_id == 0
terrain = GameData::TerrainTag.try_get(@terrain_tags[tile_id])
passage = @passages[tile_id]
if terrain
@@ -232,34 +232,34 @@ class Game_Map
# Make water tiles passable if player is surfing
return true if $PokemonGlobal.surfing && terrain.can_surf && !terrain.waterfall
# Prevent cycling in really tall grass/on ice
return false if $PokemonGlobal.bicycle && terrain.must_walk
return false if $PokemonGlobal.bicycle && (terrain.must_walk || terrain.must_walk_or_run)
# Depend on passability of bridge tile if on bridge
if terrain.bridge && $PokemonGlobal.bridge > 0
return (passage & bit == 0 && passage & 0x0f != 0x0f)
end
end
next if terrain&.ignore_passability
# Regular passability checks
if !terrain || !terrain.ignore_passability
return false if passage & bit != 0 || passage & 0x0f == 0x0f
return true if @priorities[tile_id] == 0
end
return false if passage & bit != 0 || passage & 0x0f == 0x0f
return true if @priorities[tile_id] == 0
end
return true
end
# Returns whether the position x,y is fully passable (there is no blocking
# event there, and the tile is fully passable in all directions)
# event there, and the tile is fully passable in all directions).
def passableStrict?(x, y, d, self_event = nil)
return false if !valid?(x, y)
for event in events.values
events.each_value do |event|
next if event == self_event || event.tile_id < 0 || event.through
next if !event.at_coordinate?(x, y)
next if GameData::TerrainTag.try_get(@terrain_tags[event.tile_id]).ignore_passability
return false if @passages[event.tile_id] & 0x0f != 0
return true if @priorities[event.tile_id] == 0
end
for i in [2, 1, 0]
[2, 1, 0].each do |i|
tile_id = data[x, y, i]
next if tile_id == 0
next if GameData::TerrainTag.try_get(@terrain_tags[tile_id]).ignore_passability
return false if @passages[tile_id] & 0x0f != 0
return true if @priorities[tile_id] == 0
@@ -267,9 +267,10 @@ class Game_Map
return true
end
def bush?(x,y)
for i in [2, 1, 0]
def bush?(x, y)
[2, 1, 0].each do |i|
tile_id = data[x, y, i]
next if tile_id == 0
return false if GameData::TerrainTag.try_get(@terrain_tags[tile_id]).bridge &&
$PokemonGlobal.bridge > 0
return true if @passages[tile_id] & 0x40 == 0x40
@@ -277,9 +278,10 @@ class Game_Map
return false
end
def deepBush?(x,y)
for i in [2, 1, 0]
def deepBush?(x, y)
[2, 1, 0].each do |i|
tile_id = data[x, y, i]
next if tile_id == 0
terrain = GameData::TerrainTag.try_get(@terrain_tags[tile_id])
return false if terrain.bridge && $PokemonGlobal.bridge > 0
return true if terrain.deep_bush && @passages[tile_id] & 0x40 == 0x40
@@ -287,19 +289,21 @@ class Game_Map
return false
end
def counter?(x,y)
for i in [2, 1, 0]
def counter?(x, y)
[2, 1, 0].each do |i|
tile_id = data[x, y, i]
next if tile_id == 0
passage = @passages[tile_id]
return true if passage & 0x80 == 0x80
end
return false
end
def terrain_tag(x,y,countBridge=false)
def terrain_tag(x, y, countBridge = false)
if valid?(x, y)
for i in [2, 1, 0]
[2, 1, 0].each do |i|
tile_id = data[x, y, i]
next if tile_id == 0
terrain = GameData::TerrainTag.try_get(@terrain_tags[tile_id])
next if terrain.id == :None || terrain.ignore_passability
next if !countBridge && terrain.bridge && $PokemonGlobal.bridge == 0
@@ -310,8 +314,8 @@ class Game_Map
end
# Unused.
def check_event(x,y)
for event in self.events.values
def check_event(x, y)
self.events.each_value do |event|
return event.id if event.at_coordinate?(x, y)
end
end
@@ -319,21 +323,21 @@ class Game_Map
def display_x=(value)
return if @display_x == value
@display_x = value
if GameData::MapMetadata.exists?(self.map_id) && GameData::MapMetadata.get(self.map_id).snap_edges
max_x = (self.width - Graphics.width*1.0/TILE_WIDTH) * REAL_RES_X
if metadata&.snap_edges
max_x = (self.width - (Graphics.width.to_f / TILE_WIDTH)) * REAL_RES_X
@display_x = [0, [@display_x, max_x].min].max
end
$MapFactory.setMapsInRange if $MapFactory
$map_factory&.setMapsInRange
end
def display_y=(value)
return if @display_y == value
@display_y = value
if GameData::MapMetadata.exists?(self.map_id) && GameData::MapMetadata.get(self.map_id).snap_edges
max_y = (self.height - Graphics.height*1.0/TILE_HEIGHT) * REAL_RES_Y
if metadata&.snap_edges
max_y = (self.height - (Graphics.height.to_f / TILE_HEIGHT)) * REAL_RES_Y
@display_y = [0, [@display_y, max_y].min].max
end
$MapFactory.setMapsInRange if $MapFactory
$map_factory&.setMapsInRange
end
def scroll_up(distance)
@@ -352,90 +356,135 @@ class Game_Map
self.display_x += distance
end
def start_scroll(direction, distance, speed)
@scroll_direction = direction
if direction==2 || direction==8 # down or up
@scroll_rest = distance * REAL_RES_Y
else
@scroll_rest = distance * REAL_RES_X
# speed is:
# 1: moves 1 tile in 1.6 seconds
# 2: moves 1 tile in 0.8 seconds
# 3: moves 1 tile in 0.4 seconds
# 4: moves 1 tile in 0.2 seconds
# 5: moves 1 tile in 0.1 seconds
# 6: moves 1 tile in 0.05 seconds
def start_scroll(direction, distance, speed = 4)
return if direction <= 0 || direction == 5 || direction >= 10
if [1, 3, 4, 6, 7, 9].include?(direction) # horizontal
@scroll_distance_x = distance
@scroll_distance_x *= -1 if [1, 4, 7].include?(direction)
end
if [1, 2, 3, 7, 8, 9].include?(direction) # vertical
@scroll_distance_y = distance
@scroll_distance_y *= -1 if [7, 8, 9].include?(direction)
end
@scroll_speed = speed
@scroll_start_x = display_x
@scroll_start_y = display_y
@scroll_timer_start = System.uptime
end
# The two distances can be positive or negative.
def start_scroll_custom(distance_x, distance_y, speed = 4)
return if distance_x == 0 && distance_y == 0
@scroll_distance_x = distance_x
@scroll_distance_y = distance_y
@scroll_speed = speed
@scroll_start_x = display_x
@scroll_start_y = display_y
@scroll_timer_start = System.uptime
end
def scrolling?
return @scroll_rest > 0
return (@scroll_distance_x || 0) != 0 || (@scroll_distance_y || 0) != 0
end
def start_fog_tone_change(tone,duration)
# duration is time in 1/20ths of a second.
def start_fog_tone_change(tone, duration)
if duration == 0
@fog_tone = tone.clone
return
end
@fog_tone_initial = @fog_tone.clone
@fog_tone_target = tone.clone
@fog_tone_duration = duration
if @fog_tone_duration == 0
@fog_tone = @fog_tone_target.clone
end
@fog_tone_duration = duration / 20.0
@fog_tone_timer_start = $stats.play_time
end
def start_fog_opacity_change(opacity,duration)
@fog_opacity_target = opacity*1.0
@fog_opacity_duration = duration
if @fog_opacity_duration==0
@fog_opacity = @fog_opacity_target
# duration is time in 1/20ths of a second.
def start_fog_opacity_change(opacity, duration)
if duration == 0
@fog_opacity = opacity.to_f
return
end
@fog_opacity_initial = @fog_opacity
@fog_opacity_target = opacity.to_f
@fog_opacity_duration = duration / 20.0
@fog_opacity_timer_start = $stats.play_time
end
def set_tile(x, y, layer, id = 0)
self.data[x, y, layer] = id
end
def erase_tile(x, y, layer)
set_tile(x, y, layer, 0)
end
def refresh
for event in @events.values
@events.each_value do |event|
event.refresh
end
for common_event in @common_events.values
@common_events.each_value do |common_event|
common_event.refresh
end
@need_refresh = false
end
def update
# refresh maps if necessary
if $MapFactory
for i in $MapFactory.maps
i.refresh if i.need_refresh
end
$MapFactory.setCurrentMap
uptime_now = System.uptime
play_now = $stats.play_time
# Refresh maps if necessary
if $map_factory
$map_factory.maps.each { |i| i.refresh if i.need_refresh }
$map_factory.setCurrentMap
end
# If scrolling
if @scroll_rest>0
distance = (1<<@scroll_speed)*40.0/Graphics.frame_rate
distance = @scroll_rest if distance>@scroll_rest
case @scroll_direction
when 2 then scroll_down(distance)
when 4 then scroll_left(distance)
when 6 then scroll_right(distance)
when 8 then scroll_up(distance)
end
@scroll_rest -= distance
if (@scroll_distance_x || 0) != 0
duration = @scroll_distance_x.abs * TILE_WIDTH.to_f / (10 * (2**@scroll_speed))
scroll_offset = lerp(0, @scroll_distance_x, duration, @scroll_timer_start, uptime_now)
self.display_x = @scroll_start_x + (scroll_offset * REAL_RES_X)
@scroll_distance_x = 0 if scroll_offset == @scroll_distance_x
end
if (@scroll_distance_y || 0) != 0
duration = @scroll_distance_y.abs * TILE_HEIGHT.to_f / (10 * (2**@scroll_speed))
scroll_offset = lerp(0, @scroll_distance_y, duration, @scroll_timer_start, uptime_now)
self.display_y = @scroll_start_y + (scroll_offset * REAL_RES_Y)
@scroll_distance_y = 0 if scroll_offset == @scroll_distance_y
end
# Only update events that are on-screen
for event in @events.values
event.update
if !$game_temp.in_menu
@events.each_value { |event| event.update }
end
# Update common events
for common_event in @common_events.values
common_event.update
end
@common_events.each_value { |common_event| common_event.update }
# Update fog
@fog_ox -= @fog_sx/8.0
@fog_oy -= @fog_sy/8.0
if @fog_tone_duration>=1
d = @fog_tone_duration
target = @fog_tone_target
@fog_tone.red = (@fog_tone.red * (d - 1) + target.red) / d
@fog_tone.green = (@fog_tone.green * (d - 1) + target.green) / d
@fog_tone.blue = (@fog_tone.blue * (d - 1) + target.blue) / d
@fog_tone.gray = (@fog_tone.gray * (d - 1) + target.gray) / d
@fog_tone_duration -= 1
@fog_scroll_last_update_timer = uptime_now if !@fog_scroll_last_update_timer
scroll_mult = (uptime_now - @fog_scroll_last_update_timer) * 5
@fog_ox -= @fog_sx * scroll_mult
@fog_oy -= @fog_sy * scroll_mult
@fog_scroll_last_update_timer = uptime_now
if @fog_tone_timer_start
@fog_tone.red = lerp(@fog_tone_initial.red, @fog_tone_target.red, @fog_tone_duration, @fog_tone_timer_start, play_now)
@fog_tone.green = lerp(@fog_tone_initial.green, @fog_tone_target.green, @fog_tone_duration, @fog_tone_timer_start, play_now)
@fog_tone.blue = lerp(@fog_tone_initial.blue, @fog_tone_target.blue, @fog_tone_duration, @fog_tone_timer_start, play_now)
@fog_tone.gray = lerp(@fog_tone_initial.gray, @fog_tone_target.gray, @fog_tone_duration, @fog_tone_timer_start, play_now)
if play_now - @fog_tone_timer_start >= @fog_tone_duration
@fog_tone_initial = nil
@fog_tone_timer_start = nil
end
end
if @fog_opacity_duration >= 1
d = @fog_opacity_duration
@fog_opacity = (@fog_opacity * (d - 1) + @fog_opacity_target) / d
@fog_opacity_duration -= 1
if @fog_opacity_timer_start
@fog_opacity = lerp(@fog_opacity_initial, @fog_opacity_target, @fog_opacity_duration, @fog_opacity_timer_start, play_now)
if play_now - @fog_opacity_timer_start >= @fog_opacity_duration
@fog_opacity_initial = nil
@fog_opacity_timer_start = nil
end
end
end
end
@@ -443,26 +492,74 @@ end
#===============================================================================
#
#===============================================================================
def pbScrollMap(direction,distance,speed)
if speed==0
case direction
when 2 then $game_map.scroll_down(distance * Game_Map::REAL_RES_Y)
when 4 then $game_map.scroll_left(distance * Game_Map::REAL_RES_X)
when 6 then $game_map.scroll_right(distance * Game_Map::REAL_RES_X)
when 8 then $game_map.scroll_up(distance * Game_Map::REAL_RES_Y)
# Scroll the map in the given direction by the given distance at the (optional)
# given speed.
def pbScrollMap(direction, distance, speed = 4)
if speed == 0
if [1, 2, 3].include?(direction)
$game_map.scroll_down(distance * Game_Map::REAL_RES_Y)
elsif [7, 8, 9].include?(direction)
$game_map.scroll_up(distance * Game_Map::REAL_RES_Y)
end
if [3, 6, 9].include?(direction)
$game_map.scroll_right(distance * Game_Map::REAL_RES_X)
elsif [1, 4, 7].include?(direction)
$game_map.scroll_left(distance * Game_Map::REAL_RES_X)
end
else
$game_map.start_scroll(direction, distance, speed)
oldx = $game_map.display_x
oldy = $game_map.display_y
loop do
Graphics.update
Input.update
break if !$game_map.scrolling?
pbUpdateSceneMap
break if $game_map.display_x==oldx && $game_map.display_y==oldy
oldx = $game_map.display_x
oldy = $game_map.display_y
break if !$game_map.scrolling?
end
end
end
# Scroll the map to center on the given coordinates at the (optional) given
# speed. The scroll can happen in up to two parts, depending on where the target
# is relative to the current location: an initial diagonal movement and a
# following cardinal (vertical/horizontal) movement.
def pbScrollMapTo(x, y, speed = 4)
if !$game_map.valid?(x, y)
print "pbScrollMapTo: given x,y is invalid"
return
elsif !(0..6).include?(speed)
print "pbScrollMapTo: invalid speed (0-6 only)"
return
end
# Get tile coordinates that the screen is currently scrolled to
screen_offset_x = (Graphics.width - Game_Map::TILE_WIDTH) * Game_Map::X_SUBPIXELS / 2
screen_offset_y = (Graphics.height - Game_Map::TILE_HEIGHT) * Game_Map::Y_SUBPIXELS / 2
current_tile_x = ($game_map.display_x + screen_offset_x) / Game_Map::REAL_RES_X
current_tile_y = ($game_map.display_y + screen_offset_y) / Game_Map::REAL_RES_Y
offset_x = x - current_tile_x
offset_y = y - current_tile_y
return if offset_x == 0 && offset_y == 0
if speed == 0
if offset_y > 0
$game_map.scroll_down(offset_y.abs * Game_Map::REAL_RES_Y)
elsif offset_y < 0
$game_map.scroll_up(offset_y.abs * Game_Map::REAL_RES_Y)
end
if offset_x > 0
$game_map.scroll_right(offset_x.abs * Game_Map::REAL_RES_X)
elsif offset_x < 0
$game_map.scroll_left(offset_x.abs * Game_Map::REAL_RES_X)
end
else
$game_map.start_scroll_custom(offset_x, offset_y, speed)
loop do
Graphics.update
Input.update
pbUpdateSceneMap
break if !$game_map.scrolling?
end
end
end
# Scroll the map to center on the player at the (optional) given speed.
def pbScrollMapToPlayer(speed = 4)
pbScrollMapTo($game_player.x, $game_player.y, speed)
end

View File

@@ -1,5 +1,5 @@
#===============================================================================
# Map Factory (allows multiple maps to be loaded at once and connected)
# Map Factory (allows multiple maps to be loaded at once and connected).
#===============================================================================
class PokemonMapFactory
attr_reader :maps
@@ -19,7 +19,7 @@ class PokemonMapFactory
@maps[0] = Game_Map.new
@mapIndex = 0
oldID = ($game_map) ? $game_map.map_id : 0
setMapChanging(id,@maps[0]) if oldID!=0 && oldID!=@maps[0].map_id
setMapChanging(id, @maps[0]) if oldID != 0 && oldID != @maps[0].map_id
$game_map = @maps[0]
@maps[0].setup(id)
setMapsInRange
@@ -27,33 +27,33 @@ class PokemonMapFactory
end
def map
@mapIndex = 0 if !@mapIndex || @mapIndex<0
@mapIndex = 0 if !@mapIndex || @mapIndex < 0
return @maps[@mapIndex] if @maps[@mapIndex]
raise "No maps in save file... (mapIndex=#{@mapIndex})" if @maps.length==0
raise "No maps in save file... (mapIndex=#{@mapIndex})" if @maps.length == 0
if @maps[0]
echoln("Using next map, may be incorrect (mapIndex=#{@mapIndex}, length=#{@maps.length})")
echoln "Using next map, may be incorrect (mapIndex=#{@mapIndex}, length=#{@maps.length})"
return @maps[0]
end
raise "No maps in save file... (all maps empty; mapIndex=#{@mapIndex})"
end
def hasMap?(id)
for map in @maps
return true if map.map_id==id
@maps.each do |map|
return true if map.map_id == id
end
return false
end
def getMapIndex(id)
for i in 0...@maps.length
return i if @maps[i].map_id==id
@maps.length.times do |i|
return i if @maps[i].map_id == id
end
return -1
end
def getMap(id,add=true)
for map in @maps
return map if map.map_id==id
def getMap(id, add = true)
@maps.each do |map|
return map if map.map_id == id
end
map = Game_Map.new
map.setup(id)
@@ -62,31 +62,29 @@ class PokemonMapFactory
end
def getMapNoAdd(id)
return getMap(id,false)
return getMap(id, false)
end
def getNewMap(playerX,playerY)
id = $game_map.map_id
conns = MapFactoryHelper.getMapConnections
if conns[id]
for conn in conns[id]
mapidB = nil
newx = 0
newy = 0
if conn[0] == id
mapidB = conn[3]
mapB = MapFactoryHelper.getMapDims(conn[3])
newx = conn[4] - conn[1] + playerX
newy = conn[5] - conn[2] + playerY
else
mapidB = conn[0]
mapB = MapFactoryHelper.getMapDims(conn[0])
newx = conn[1] - conn[4] + playerX
newy = conn[2] - conn[5] + playerY
end
if newx >= 0 && newx < mapB[0] && newy >= 0 && newy < mapB[1]
return [getMap(mapidB), newx, newy]
end
def getNewMap(playerX, playerY, map_id = nil)
id = map_id || $game_map.map_id
MapFactoryHelper.eachConnectionForMap(id) do |conn|
mapidB = nil
newx = 0
newy = 0
if conn[0] == id
mapidB = conn[3]
mapB = MapFactoryHelper.getMapDims(conn[3])
newx = conn[4] - conn[1] + playerX
newy = conn[5] - conn[2] + playerY
else
mapidB = conn[0]
mapB = MapFactoryHelper.getMapDims(conn[0])
newx = conn[1] - conn[4] + playerX
newy = conn[2] - conn[5] + playerY
end
if newx >= 0 && newx < mapB[0] && newy >= 0 && newy < mapB[1]
return [getMapNoAdd(mapidB), newx, newy] if map_id
return [getMap(mapidB), newx, newy]
end
end
return nil
@@ -95,17 +93,17 @@ class PokemonMapFactory
# Detects whether the player has moved onto a connected map, and if so, causes
# their transfer to that map.
def setCurrentMap
return if $game_player.moving?
return if $game_map.valid?($game_player.x,$game_player.y)
newmap = getNewMap($game_player.x,$game_player.y)
return if $game_player.moving? || $game_player.jumping?
return if $game_map.valid?($game_player.x, $game_player.y)
newmap = getNewMap($game_player.x, $game_player.y)
return if !newmap
oldmap=$game_map.map_id
if oldmap!=0 && oldmap!=newmap[0].map_id
setMapChanging(newmap[0].map_id,newmap[0])
oldmap = $game_map.map_id
if oldmap != 0 && oldmap != newmap[0].map_id
setMapChanging(newmap[0].map_id, newmap[0])
end
$game_map = newmap[0]
@mapIndex = getMapIndex($game_map.map_id)
$game_player.moveto(newmap[1],newmap[2])
$game_player.moveto(newmap[1], newmap[2])
$game_map.update
pbAutoplayOnTransition
$game_map.refresh
@@ -117,148 +115,128 @@ class PokemonMapFactory
return if @fixup
@fixup = true
id = $game_map.map_id
conns = MapFactoryHelper.getMapConnections
if conns[id]
for conn in conns[id]
if conn[0] == id
mapA = getMap(conn[0])
newdispx = (conn[4] - conn[1]) * Game_Map::REAL_RES_X + mapA.display_x
newdispy = (conn[5] - conn[2]) * Game_Map::REAL_RES_Y + mapA.display_y
if hasMap?(conn[3]) || MapFactoryHelper.mapInRangeById?(conn[3], newdispx, newdispy)
mapB = getMap(conn[3])
mapB.display_x = newdispx if mapB.display_x != newdispx
mapB.display_y = newdispy if mapB.display_y != newdispy
end
else
mapA = getMap(conn[3])
newdispx = (conn[1] - conn[4]) * Game_Map::REAL_RES_X + mapA.display_x
newdispy = (conn[2] - conn[5]) * Game_Map::REAL_RES_Y + mapA.display_y
if hasMap?(conn[0]) || MapFactoryHelper.mapInRangeById?(conn[0], newdispx, newdispy)
mapB = getMap(conn[0])
mapB.display_x = newdispx if mapB.display_x != newdispx
mapB.display_y = newdispy if mapB.display_y != newdispy
end
MapFactoryHelper.eachConnectionForMap(id) do |conn|
if conn[0] == id
mapA = getMap(conn[0])
newdispx = ((conn[4] - conn[1]) * Game_Map::REAL_RES_X) + mapA.display_x
newdispy = ((conn[5] - conn[2]) * Game_Map::REAL_RES_Y) + mapA.display_y
if hasMap?(conn[3]) || MapFactoryHelper.mapInRangeById?(conn[3], newdispx, newdispy)
mapB = getMap(conn[3])
mapB.display_x = newdispx if mapB.display_x != newdispx
mapB.display_y = newdispy if mapB.display_y != newdispy
end
else
mapA = getMap(conn[3])
newdispx = ((conn[1] - conn[4]) * Game_Map::REAL_RES_X) + mapA.display_x
newdispy = ((conn[2] - conn[5]) * Game_Map::REAL_RES_Y) + mapA.display_y
if hasMap?(conn[0]) || MapFactoryHelper.mapInRangeById?(conn[0], newdispx, newdispy)
mapB = getMap(conn[0])
mapB.display_x = newdispx if mapB.display_x != newdispx
mapB.display_y = newdispy if mapB.display_y != newdispy
end
end
end
@fixup = false
end
def setMapChanging(newID,newMap)
Events.onMapChanging.trigger(self,newID,newMap)
def setMapChanging(newID, newMap)
EventHandlers.trigger(:on_leave_map, newID, newMap)
end
def setMapChanged(prevMap)
Events.onMapChange.trigger(self,prevMap)
EventHandlers.trigger(:on_enter_map, prevMap)
@mapChanged = true
end
def setSceneStarted(scene)
Events.onMapSceneChange.trigger(self,scene,@mapChanged)
EventHandlers.trigger(:on_map_or_spriteset_change, scene, @mapChanged)
@mapChanged = false
end
# Similar to Game_Player#passable?, but supports map connections
def isPassableFromEdge?(x, y)
def isPassableFromEdge?(x, y, dir = 0)
return true if $game_map.valid?(x, y)
newmap = getNewMap(x, y)
newmap = getNewMap(x, y, $game_map.map_id)
return false if !newmap
return isPassable?(newmap[0].map_id, newmap[1], newmap[2])
return isPassable?(newmap[0].map_id, newmap[1], newmap[2], dir)
end
def isPassable?(mapID, x, y, thisEvent = nil)
def isPassable?(mapID, x, y, dir = 0, thisEvent = nil)
thisEvent = $game_player if !thisEvent
map = getMapNoAdd(mapID)
return false if !map
return false if !map.valid?(x, y)
return true if thisEvent.through
# Check passability of tile
if thisEvent.is_a?(Game_Player)
return false unless ($DEBUG && Input.press?(Input::CTRL)) ||
map.passable?(x, y, 0, thisEvent)
else
return false unless map.passable?(x, y, 0, thisEvent)
end
return true if $DEBUG && Input.press?(Input::CTRL) && thisEvent.is_a?(Game_Player)
return false if !map.passable?(x, y, dir, thisEvent)
# Check passability of event(s) in that spot
for event in map.events.values
map.events.each_value do |event|
next if event == thisEvent || !event.at_coordinate?(x, y)
return false if !event.through && event.character_name != ""
end
# Check passability of player
if !thisEvent.is_a?(Game_Player)
if $game_map.map_id == mapID && $game_player.x == x && $game_player.y == y
return false if !$game_player.through && $game_player.character_name != ""
end
if !thisEvent.is_a?(Game_Player) &&
$game_map.map_id == mapID && $game_player.x == x && $game_player.y == y &&
!$game_player.through && $game_player.character_name != ""
return false
end
return true
end
# Only used by dependent events
def isPassableStrict?(mapID,x,y,thisEvent=nil)
# Only used by follower events
def isPassableStrict?(mapID, x, y, thisEvent = nil)
thisEvent = $game_player if !thisEvent
map = getMapNoAdd(mapID)
return false if !map
return false if !map.valid?(x,y)
return false if !map.valid?(x, y)
return true if thisEvent.through
if thisEvent==$game_player
if !($DEBUG && Input.press?(Input::CTRL))
return false if !map.passableStrict?(x,y,0,thisEvent)
end
else
return false if !map.passableStrict?(x,y,0,thisEvent)
end
for event in map.events.values
return true if $DEBUG && Input.press?(Input::CTRL) && thisEvent.is_a?(Game_Player)
return false if !map.passableStrict?(x, y, 0, thisEvent)
map.events.each_value do |event|
next if event == thisEvent || !event.at_coordinate?(x, y)
return false if !event.through && event.character_name!=""
return false if !event.through && event.character_name != ""
end
return true
end
def getTerrainTag(mapid,x,y,countBridge=false)
def getTerrainTag(mapid, x, y, countBridge = false)
map = getMapNoAdd(mapid)
return map.terrain_tag(x,y,countBridge)
return map.terrain_tag(x, y, countBridge)
end
# NOTE: Assumes the event is 1x1 tile in size. Only returns one terrain tag.
def getFacingTerrainTag(dir=nil,event=nil)
tile = getFacingTile(dir,event)
def getFacingTerrainTag(dir = nil, event = nil)
tile = getFacingTile(dir, event)
return GameData::TerrainTag.get(:None) if !tile
return getTerrainTag(tile[0],tile[1],tile[2])
return getTerrainTag(tile[0], tile[1], tile[2])
end
def getTerrainTagFromCoords(mapid,x,y,countBridge=false)
tile = getRealTilePos(mapid,x,y)
def getTerrainTagFromCoords(mapid, x, y, countBridge = false)
tile = getRealTilePos(mapid, x, y)
return GameData::TerrainTag.get(:None) if !tile
return getTerrainTag(tile[0],tile[1],tile[2])
return getTerrainTag(tile[0], tile[1], tile[2])
end
def areConnected?(mapID1, mapID2)
return true if mapID1 == mapID2
conns = MapFactoryHelper.getMapConnections
if conns[mapID1]
for conn in conns[mapID1]
return true if conn[0] == mapID2 || conn[3] == mapID2
end
end
return false
return MapFactoryHelper.mapsConnected?(mapID1, mapID2)
end
# Returns the coordinate change to go from this position to other position
def getRelativePos(thisMapID, thisX, thisY, otherMapID, otherX, otherY)
if thisMapID == otherMapID # Both events share the same map
return [otherX - thisX, otherY - thisY]
end
conns = MapFactoryHelper.getMapConnections
if conns[thisMapID]
for conn in conns[thisMapID]
if conn[0] == otherMapID
posX = thisX + conn[1] - conn[4] + otherX
posY = thisY + conn[2] - conn[5] + otherY
return [posX, posY]
elsif conn[1] == otherMapID
posX = thisX + conn[4] - conn[1] + otherX
posY = thisY + conn[5] - conn[2] + otherY
return [posX, posY]
end
MapFactoryHelper.eachConnectionForMap(thisMapID) do |conn|
if conn[0] == otherMapID
posX = conn[4] - conn[1] + otherX - thisX
posY = conn[5] - conn[2] + otherY - thisY
return [posX, posY]
elsif conn[3] == otherMapID
posX = conn[1] - conn[4] + otherX - thisX
posY = conn[2] - conn[5] + otherY - thisY
return [posX, posY]
end
end
return [0, 0]
@@ -267,38 +245,37 @@ class PokemonMapFactory
# Gets the distance from this event to another event. Example: If this event's
# coordinates are (2,5) and the other event's coordinates are (5,1), returns
# the array (3,-4), because (5-2=3) and (1-5=-4).
def getThisAndOtherEventRelativePos(thisEvent,otherEvent)
return [0,0] if !thisEvent || !otherEvent
return getRelativePos(
thisEvent.map.map_id,thisEvent.x,thisEvent.y,
otherEvent.map.map_id,otherEvent.x,otherEvent.y)
def getThisAndOtherEventRelativePos(thisEvent, otherEvent)
return [0, 0] if !thisEvent || !otherEvent
return getRelativePos(thisEvent.map.map_id, thisEvent.x, thisEvent.y,
otherEvent.map.map_id, otherEvent.x, otherEvent.y)
end
def getThisAndOtherPosRelativePos(thisEvent,otherMapID,otherX,otherY)
return [0,0] if !thisEvent
return getRelativePos(
thisEvent.map.map_id,thisEvent.x,thisEvent.y,otherMapID,otherX,otherY)
def getThisAndOtherPosRelativePos(thisEvent, otherMapID, otherX, otherY)
return [0, 0] if !thisEvent
return getRelativePos(thisEvent.map.map_id, thisEvent.x, thisEvent.y,
otherMapID, otherX, otherY)
end
# Unused
def getOffsetEventPos(event,xOffset,yOffset)
def getOffsetEventPos(event, xOffset, yOffset)
event = $game_player if !event
return nil if !event
return getRealTilePos(event.map.map_id,event.x+xOffset,event.y+yOffset)
return getRealTilePos(event.map.map_id, event.x + xOffset, event.y + yOffset)
end
# NOTE: Assumes the event is 1x1 tile in size. Only returns one tile.
def getFacingTile(direction=nil,event=nil,steps=1)
event = $game_player if event==nil
return [0,0,0] if !event
def getFacingTile(direction = nil, event = nil, steps = 1)
event = $game_player if event.nil?
return [0, 0, 0] if !event
x = event.x
y = event.y
id = event.map.map_id
direction = event.direction if direction==nil
return getFacingTileFromPos(id,x,y,direction,steps)
direction = event.direction if direction.nil?
return getFacingTileFromPos(id, x, y, direction, steps)
end
def getFacingTileFromPos(mapID,x,y,direction=0,steps=1)
def getFacingTileFromPos(mapID, x, y, direction = 0, steps = 1)
id = mapID
case direction
when 1
@@ -322,38 +299,35 @@ class PokemonMapFactory
x += steps
y -= steps
else
return [id,x,y]
return [id, x, y]
end
return getRealTilePos(mapID,x,y)
return getRealTilePos(mapID, x, y)
end
def getRealTilePos(mapID, x, y)
id = mapID
return [id, x, y] if getMapNoAdd(id).valid?(x, y)
conns = MapFactoryHelper.getMapConnections
if conns[id]
for conn in conns[id]
if conn[0] == id
newX = x + conn[4] - conn[1]
newY = y + conn[5] - conn[2]
next if newX < 0 || newY < 0
dims = MapFactoryHelper.getMapDims(conn[3])
next if newX >= dims[0] || newY >= dims[1]
return [conn[3], newX, newY]
else
newX = x + conn[1] - conn[4]
newY = y + conn[2] - conn[5]
next if newX < 0 || newY < 0
dims = MapFactoryHelper.getMapDims(conn[0])
next if newX >= dims[0] || newY >= dims[1]
return [conn[0], newX, newY]
end
MapFactoryHelper.eachConnectionForMap(id) do |conn|
if conn[0] == id
newX = x + conn[4] - conn[1]
newY = y + conn[5] - conn[2]
next if newX < 0 || newY < 0
dims = MapFactoryHelper.getMapDims(conn[3])
next if newX >= dims[0] || newY >= dims[1]
return [conn[3], newX, newY]
else
newX = x + conn[1] - conn[4]
newY = y + conn[2] - conn[5]
next if newX < 0 || newY < 0
dims = MapFactoryHelper.getMapDims(conn[0])
next if newX >= dims[0] || newY >= dims[1]
return [conn[0], newX, newY]
end
end
return nil
end
def getFacingCoords(x,y,direction=0,steps=1)
def getFacingCoords(x, y, direction = 0, steps = 1)
case direction
when 1
x -= steps
@@ -376,36 +350,29 @@ class PokemonMapFactory
x += steps
y -= steps
end
return [x,y]
return [x, y]
end
def updateMaps(scene)
updateMapsInternal
$MapFactory.setSceneStarted(scene) if @mapChanged
setSceneStarted(scene) if @mapChanged
end
def updateMapsInternal
return if $game_player.moving?
if !MapFactoryHelper.hasConnections?($game_map.map_id)
return if @maps.length==1
for i in 0...@maps.length
@maps[i] = nil if $game_map.map_id!=@maps[i].map_id
end
@maps.compact!
return if @maps.length == 1
@maps.delete_if { |map| map.map_id != $game_map.map_id }
@mapIndex = getMapIndex($game_map.map_id)
return
end
old_num_maps = @maps.length
@maps.delete_if { |map| !MapFactoryHelper.mapsConnected?($game_map.map_id, map.map_id) }
@mapIndex = getMapIndex($game_map.map_id) if @maps.length != old_num_maps
setMapsInRange
deleted = false
for i in 0...@maps.length
next if MapFactoryHelper.mapInRange?(@maps[i])
@maps[i] = nil
deleted = true
end
if deleted
@maps.compact!
@mapIndex = getMapIndex($game_map.map_id)
end
old_num_maps = @maps.length
@maps.delete_if { |map| !MapFactoryHelper.mapInRange?(map) }
@mapIndex = getMapIndex($game_map.map_id) if @maps.length != old_num_maps
end
end
@@ -417,12 +384,14 @@ module MapFactoryHelper
@@MapConnections = nil
@@MapDims = nil
def self.clear
module_function
def clear
@@MapConnections = nil
@@MapDims = nil
end
def self.getMapConnections
def getMapConnections
if !@@MapConnections
@@MapConnections = []
conns = load_data("Data/map_connections.dat")
@@ -460,22 +429,35 @@ module MapFactoryHelper
return @@MapConnections
end
def self.hasConnections?(id)
def hasConnections?(id)
conns = MapFactoryHelper.getMapConnections
return conns[id] ? true : false
end
# Gets the height and width of the map with id
def self.getMapDims(id)
def mapsConnected?(id1, id2)
MapFactoryHelper.eachConnectionForMap(id1) do |conn|
return true if conn[0] == id2 || conn[3] == id2
end
return false
end
def eachConnectionForMap(id)
conns = MapFactoryHelper.getMapConnections
return if !conns[id]
conns[id].each { |conn| yield conn }
end
# Gets the height and width of the map with id.
def getMapDims(id)
# Create cache if doesn't exist
@@MapDims = [] if !@@MapDims
# Add map to cache if can't be found
if !@@MapDims[id]
begin
map = load_data(sprintf("Data/Map%03d.rxdata", id))
@@MapDims[id] = [map.width,map.height]
@@MapDims[id] = [map.width, map.height]
rescue
@@MapDims[id] = [0,0]
@@MapDims[id] = [0, 0]
end
end
# Return map in cache
@@ -484,32 +466,32 @@ module MapFactoryHelper
# Returns the X or Y coordinate of an edge on the map with id.
# Considers the special strings "N","W","E","S"
def self.getMapEdge(id,edge)
return 0 if edge=="N" || edge=="W"
def getMapEdge(id, edge)
return 0 if ["N", "W"].include?(edge)
dims = getMapDims(id) # Get dimensions
return dims[0] if edge=="E"
return dims[1] if edge=="S"
return dims[0] if edge == "E"
return dims[1] if edge == "S"
return dims[0] # real dimension (use width)
end
def self.mapInRange?(map)
def mapInRange?(map)
range = 6 # Number of tiles
dispx = map.display_x
dispy = map.display_y
return false if dispx >= (map.width + range) * Game_Map::REAL_RES_X
return false if dispy >= (map.height + range) * Game_Map::REAL_RES_Y
return false if dispx <= -(Graphics.width + range * Game_Map::TILE_WIDTH) * Game_Map::X_SUBPIXELS
return false if dispy <= -(Graphics.height + range * Game_Map::TILE_HEIGHT) * Game_Map::Y_SUBPIXELS
return false if dispx <= -(Graphics.width + (range * Game_Map::TILE_WIDTH)) * Game_Map::X_SUBPIXELS
return false if dispy <= -(Graphics.height + (range * Game_Map::TILE_HEIGHT)) * Game_Map::Y_SUBPIXELS
return true
end
def self.mapInRangeById?(id,dispx,dispy)
def mapInRangeById?(id, dispx, dispy)
range = 6 # Number of tiles
dims = MapFactoryHelper.getMapDims(id)
return false if dispx >= (dims[0] + range) * Game_Map::REAL_RES_X
return false if dispy >= (dims[1] + range) * Game_Map::REAL_RES_Y
return false if dispx <= -(Graphics.width + range * Game_Map::TILE_WIDTH) * Game_Map::X_SUBPIXELS
return false if dispy <= -(Graphics.height + range * Game_Map::TILE_HEIGHT) * Game_Map::Y_SUBPIXELS
return false if dispx <= -(Graphics.width + (range * Game_Map::TILE_WIDTH)) * Game_Map::X_SUBPIXELS
return false if dispy <= -(Graphics.height + (range * Game_Map::TILE_HEIGHT)) * Game_Map::Y_SUBPIXELS
return true
end
end
@@ -519,8 +501,8 @@ end
#===============================================================================
# Unused
def updateTilesets
maps = $MapFactory.maps
for map in maps
map.updateTileset if map
maps = $map_factory.maps
maps.each do |map|
map&.updateTileset
end
end

View File

@@ -1,194 +0,0 @@
#===============================================================================
# ** Map Autoscroll
#-------------------------------------------------------------------------------
# Wachunga
# Version 1.02
# 2005-12-18
#===============================================================================
=begin
This script supplements the built-in "Scroll Map" event command with the
aim of simplifying cutscenes (and map scrolling in general). Whereas the
normal event command requires a direction and number of tiles to scroll,
Map Autoscroll scrolls the map to center on the tile whose x and y
coordinates are given.
FEATURES
- automatic map scrolling to given x,y coordinate (or player)
- destination is fixed, so it's possible to scroll to same place even if
origin is variable (e.g. moving NPC)
- variable speed (just like "Scroll Map" event command)
- diagonal scrolling supported
SETUP
Instead of a "Scroll Map" event command, use the "Call Script" command
and enter on the following on the first line:
autoscroll(x,y)
(replacing "x" and "y" with the x and y coordinates of the tile to scroll to)
To specify a scroll speed other than the default (4), use:
autoscroll(x,y,speed)
(now also replacing "speed" with the scroll speed from 1-6)
Diagonal scrolling happens automatically when the destination is diagonal
relative to the starting point (i.e., not directly up, down, left or right).
To scroll to the player, instead use the following:
autoscroll_player(speed)
Note: because of how the interpreter and the "Call Script" event command
are setup, the call to autoscroll(...) can only be on the first line of
the "Call Script" event command (and not flowing down to subsequent lines).
For example, the following call may not work as expected:
autoscroll($game_variables[1],
$game_variables[2])
(since the long argument names require dropping down to a second line)
A work-around is to setup new variables with shorter names in a preceding
(separate) "Call Script" event command:
@x = $game_variables[1]
@y = $game_variables[2]
and then use those as arguments:
autoscroll(@x,@y)
The renaming must be in a separate "Call Script" because otherwise
the call to autoscroll(...) isn't on the first line.
Originally requested by militantmilo80:
http://www.rmxp.net/forums/index.php?showtopic=29519
=end
class Interpreter
SCROLL_SPEED_DEFAULT = 4
#-----------------------------------------------------------------------------
# * Map Autoscroll to Coordinates
# x : x coordinate to scroll to and center on
# y : y coordinate to scroll to and center on
# speed : (optional) scroll speed (from 1-6, default being 4)
#-----------------------------------------------------------------------------
def autoscroll(x,y,speed=SCROLL_SPEED_DEFAULT)
if $game_map.scrolling?
return false
elsif !$game_map.valid?(x,y)
print 'Map Autoscroll: given x,y is invalid'
return command_skip
elsif !(1..6).include?(speed)
print 'Map Autoscroll: invalid speed (1-6 only)'
return command_skip
end
center_x = (Graphics.width/2 - Game_Map::TILE_WIDTH/2) * 4 # X coordinate in the center of the screen
center_y = (Graphics.height/2 - Game_Map::TILE_HEIGHT/2) * 4 # Y coordinate in the center of the screen
max_x = ($game_map.width - Graphics.width*1.0/Game_Map::TILE_WIDTH) * 4 * Game_Map::TILE_WIDTH
max_y = ($game_map.height - Graphics.height*1.0/Game_Map::TILE_HEIGHT) * 4 * Game_Map::TILE_HEIGHT
count_x = ($game_map.display_x - [0,[x*Game_Map::REAL_RES_X-center_x,max_x].min].max)/Game_Map::REAL_RES_X
count_y = ($game_map.display_y - [0,[y*Game_Map::REAL_RES_Y-center_y,max_y].min].max)/Game_Map::REAL_RES_Y
if !@diag
@diag = true
dir = nil
if count_x > 0
if count_y > 0
dir = 7
elsif count_y < 0
dir = 1
end
elsif count_x < 0
if count_y > 0
dir = 9
elsif count_y < 0
dir = 3
end
end
count = [count_x.abs,count_y.abs].min
else
@diag = false
dir = nil
if count_x != 0 && count_y != 0
return false
elsif count_x > 0
dir = 4
elsif count_x < 0
dir = 6
elsif count_y > 0
dir = 8
elsif count_y < 0
dir = 2
end
count = count_x != 0 ? count_x.abs : count_y.abs
end
$game_map.start_scroll(dir, count, speed) if dir != nil
if @diag
return false
else
return true
end
end
#-----------------------------------------------------------------------------
# * Map Autoscroll (to Player)
# speed : (optional) scroll speed (from 1-6, default being 4)
#-----------------------------------------------------------------------------
def autoscroll_player(speed=SCROLL_SPEED_DEFAULT)
autoscroll($game_player.x,$game_player.y,speed)
end
end
class Game_Map
def scroll_downright(distance)
@display_x = [@display_x + distance,
(self.width - Graphics.width*1.0/TILE_WIDTH) * REAL_RES_X].min
@display_y = [@display_y + distance,
(self.height - Graphics.height*1.0/TILE_HEIGHT) * REAL_RES_Y].min
end
def scroll_downleft(distance)
@display_x = [@display_x - distance, 0].max
@display_y = [@display_y + distance,
(self.height - Graphics.height*1.0/TILE_HEIGHT) * REAL_RES_Y].min
end
def scroll_upright(distance)
@display_x = [@display_x + distance,
(self.width - Graphics.width*1.0/TILE_WIDTH) * REAL_RES_X].min
@display_y = [@display_y - distance, 0].max
end
def scroll_upleft(distance)
@display_x = [@display_x - distance, 0].max
@display_y = [@display_y - distance, 0].max
end
def update_scrolling
# If scrolling
if @scroll_rest > 0
# Change from scroll speed to distance in map coordinates
distance = (1<<@scroll_speed)*40/Graphics.frame_rate
distance = @scroll_rest if distance>@scroll_rest
# Execute scrolling
case @scroll_direction
when 1 then scroll_downleft(distance)
when 2 then scroll_down(distance)
when 3 then scroll_downright(distance)
when 4 then scroll_left(distance)
when 6 then scroll_right(distance)
when 7 then scroll_upleft(distance)
when 8 then scroll_up(distance)
when 9 then scroll_upright(distance)
end
# Subtract distance scrolled
@scroll_rest -= distance
end
end
end

View File

@@ -1,3 +1,6 @@
#===============================================================================
#
#===============================================================================
class Game_Character
attr_reader :id
attr_reader :original_x
@@ -6,13 +9,15 @@ class Game_Character
attr_reader :y
attr_reader :real_x
attr_reader :real_y
attr_writer :x_offset # In pixels, positive shifts sprite to the right
attr_writer :y_offset # In pixels, positive shifts sprite down
attr_accessor :width
attr_accessor :height
attr_accessor :sprite_size
attr_reader :tile_id
attr_accessor :character_name
attr_accessor :character_hue
attr_reader :opacity
attr_accessor :opacity
attr_reader :blend_type
attr_accessor :direction
attr_accessor :pattern
@@ -20,13 +25,16 @@ class Game_Character
attr_accessor :lock_pattern
attr_reader :move_route_forcing
attr_accessor :through
attr_accessor :animation_id
attr_reader :animation_id
attr_accessor :animation_height
attr_accessor :animation_regular_tone
attr_accessor :transparent
attr_reader :move_speed
attr_reader :jump_speed
attr_accessor :walk_anime
attr_writer :bob_height
def initialize(map=nil)
def initialize(map = nil)
@map = map
@id = 0
@original_x = 0
@@ -35,6 +43,8 @@ class Game_Character
@y = 0
@real_x = 0
@real_y = 0
@x_offset = 0
@y_offset = 0
@width = 1
@height = 1
@sprite_size = [Game_Map::TILE_WIDTH, Game_Map::TILE_HEIGHT]
@@ -49,13 +59,14 @@ class Game_Character
@lock_pattern = false
@move_route_forcing = false
@through = false
@animation_id = 0
animation_id = 0
@transparent = false
@original_direction = 2
@original_pattern = 0
@move_type = 0
self.move_speed = 3
self.move_frequency = 6
self.jump_speed = 3
@move_route = nil
@move_route_index = 0
@original_move_route = nil
@@ -64,19 +75,32 @@ class Game_Character
@step_anime = false # Whether character should animate while still
@direction_fix = false
@always_on_top = false
@anime_count = 0
@stop_count = 0
@anime_count = 0 # Time since pattern was last changed
@stop_count = 0 # Time since character last finished moving
@jump_peak = 0 # Max height while jumping
@jump_distance = 0 # Total distance of jump
@jump_distance_left = 0 # Distance left to travel
@jump_count = 0 # Frames left in a stationary jump
@jump_fraction = 0 # How far through a jump we currently are (0-1)
@jumping_on_spot = false
@bob_height = 0
@wait_count = 0
@wait_start = nil
@moved_this_frame = false
@moveto_happened = false
@locked = false
@prelock_direction = 0
end
def animation_id=(value)
@animation_id = value
if value == 0
@animation_height = 3
@animation_regular_tone = false
end
end
def x_offset; return @x_offset || 0; end
def y_offset; return @y_offset || 0; end
def at_coordinate?(check_x, check_y)
return check_x >= @x && check_x < @x + @width &&
check_y > @y - @height && check_y <= @y
@@ -88,70 +112,61 @@ class Game_Character
end
def each_occupied_tile
for i in @x...(@x + @width)
for j in (@y - @height + 1)..@y
(@x...(@x + @width)).each do |i|
((@y - @height + 1)..@y).each do |j|
yield i, j
end
end
end
def move_speed=(val)
return if val==@move_speed
@move_speed = val
# @move_speed_real is the number of quarter-pixels to move each frame. There
# are 128 quarter-pixels per tile. By default, it is calculated from
# @move_speed and has these values (assuming 40 fps):
# 1 => 3.2 # 40 frames per tile
# 2 => 6.4 # 20 frames per tile
# 3 => 12.8 # 10 frames per tile - walking speed
# 4 => 25.6 # 5 frames per tile - running speed (2x walking speed)
# 5 => 32 # 4 frames per tile - cycling speed (1.25x running speed)
# 6 => 64 # 2 frames per tile
self.move_speed_real = (val == 6) ? 64 : (val == 5) ? 32 : (2 ** (val + 1)) * 0.8
# Time taken to traverse one tile (in seconds) for each speed:
# 1 => 1.0
# 2 => 0.5
# 3 => 0.25 # Walking speed
# 4 => 0.125 # Running speed (2x walking speed)
# 5 => 0.1 # Cycling speed (1.25x running speed)
# 6 => 0.05
case val
when 6 then @move_time = 0.05
when 5 then @move_time = 0.1
else @move_time = 2.0 / (2**val)
end
end
def move_speed_real
self.move_speed = @move_speed if !@move_speed_real
return @move_speed_real
# Takes the same values as move_speed above.
def jump_speed=(val)
@jump_speed = val
case val
when 6 then @jump_time = 0.05
when 5 then @jump_time = 0.1
else @jump_time = 2.0 / (2**val)
end
end
def move_speed_real=(val)
@move_speed_real = val * 40.0 / Graphics.frame_rate
end
def jump_speed_real
self.jump_speed_real = (2 ** (3 + 1)) * 0.8 if !@jump_speed_real # 3 is walking speed
return @jump_speed_real
end
def jump_speed_real=(val)
@jump_speed_real = val * 40.0 / Graphics.frame_rate
# Returns time in seconds for one full cycle (4 frames) of an animating
# charset to show. Two frames are shown per movement across one tile.
def pattern_update_speed
return @jump_time * 2 if jumping?
ret = @move_time * 2
ret *= 2 if @move_speed >= 5 # Cycling speed or faster; slower animation
return ret
end
def move_frequency=(val)
return if val==@move_frequency
return if val == @move_frequency
@move_frequency = val
# @move_frequency_real is the number of frames to wait between each action
# in a move route (not forced). Specifically, this is the number of frames
# to wait after the character stops moving because of the previous action.
# By default, it is calculated from @move_frequency and has these values
# (assuming 40 fps):
# 1 => 190 # 4.75 seconds
# 2 => 144 # 3.6 seconds
# 3 => 102 # 2.55 seconds
# 4 => 64 # 1.6 seconds
# 5 => 30 # 0.75 seconds
# 6 => 0 # 0 seconds, i.e. continuous movement
self.move_frequency_real = (40 - val * 2) * (6 - val)
end
def move_frequency_real
self.move_frequency = @move_frequency if !@move_frequency_real
return @move_frequency_real
end
def move_frequency_real=(val)
@move_frequency_real = val * Graphics.frame_rate / 40.0
# Time in seconds to wait between each action in a move route (not forced).
# Specifically, this is the time to wait after the character stops moving
# because of the previous action.
# 1 => 4.75 seconds
# 2 => 3.6 seconds
# 3 => 2.55 seconds
# 4 => 1.6 seconds
# 5 => 0.75 seconds
# 6 => 0 seconds, i.e. continuous movement
@command_delay = (40 - (val * 2)) * (6 - val) / 40.0
end
def bob_height
@@ -181,9 +196,10 @@ class Game_Character
@direction = @prelock_direction if !@direction_fix && @prelock_direction != 0
end
#=============================================================================
#-----------------------------------------------------------------------------
# Information from map data
#=============================================================================
#-----------------------------------------------------------------------------
def map
return (@map) ? @map : $game_map
end
@@ -193,48 +209,65 @@ class Game_Character
end
def bush_depth
return 0 if respond_to?("name") && name[/airborne/i]
return @bush_depth || 0
end
def calculate_bush_depth
if @tile_id > 0 || @always_on_top || jumping?
if @tile_id > 0 || @always_on_top || jumping? || (respond_to?("name") && name[/airborne/i])
@bush_depth = 0
else
deep_bush = regular_bush = false
return
end
this_map = (self.map.valid?(@x, @y)) ? [self.map, @x, @y] : $map_factory&.getNewMap(@x, @y, self.map.map_id)
if this_map && this_map[0].deepBush?(this_map[1], this_map[2])
xbehind = @x + (@direction == 4 ? 1 : @direction == 6 ? -1 : 0)
ybehind = @y + (@direction == 8 ? 1 : @direction == 2 ? -1 : 0)
this_map = (self.map.valid?(@x, @y)) ? [self.map, @x, @y] : $MapFactory.getNewMap(@x, @y)
if this_map[0].deepBush?(this_map[1], this_map[2]) && self.map.deepBush?(xbehind, ybehind)
@bush_depth = Game_Map::TILE_HEIGHT
elsif !moving? && this_map[0].bush?(this_map[1], this_map[2])
@bush_depth = 12
if moving?
behind_map = (self.map.valid?(xbehind, ybehind)) ? [self.map, xbehind, ybehind] : $map_factory&.getNewMap(xbehind, ybehind, self.map.map_id)
@bush_depth = Game_Map::TILE_HEIGHT if behind_map[0].deepBush?(behind_map[1], behind_map[2])
else
@bush_depth = 0
@bush_depth = Game_Map::TILE_HEIGHT
end
elsif this_map && this_map[0].bush?(this_map[1], this_map[2]) && !moving?
@bush_depth = 12
else
@bush_depth = 0
end
end
#=============================================================================
def fullPattern
case self.direction
when 2 then return self.pattern
when 4 then return self.pattern + 4
when 6 then return self.pattern + 8
when 8 then return self.pattern + 12
end
return 0
end
#-----------------------------------------------------------------------------
# Passability
#=============================================================================
def passable?(x, y, d, strict = false)
new_x = x + (d == 6 ? 1 : d == 4 ? -1 : 0)
new_y = y + (d == 2 ? 1 : d == 8 ? -1 : 0)
#-----------------------------------------------------------------------------
def passable?(x, y, dir, strict = false)
new_x = x + (dir == 6 ? 1 : dir == 4 ? -1 : 0)
new_y = y + (dir == 2 ? 1 : dir == 8 ? -1 : 0)
return false unless self.map.valid?(new_x, new_y)
return true if @through
if strict
return false unless self.map.passableStrict?(x, y, d, self)
return false unless self.map.passableStrict?(new_x, new_y, 10 - d, self)
return false unless self.map.passableStrict?(x, y, dir, self)
return false unless self.map.passableStrict?(new_x, new_y, 10 - dir, self)
else
return false unless self.map.passable?(x, y, d, self)
return false unless self.map.passable?(new_x, new_y, 10 - d, self)
return false unless self.map.passable?(x, y, dir, self)
return false unless self.map.passable?(new_x, new_y, 10 - dir, self)
end
for event in self.map.events.values
self.map.events.each_value do |event|
next if self == event || !event.at_coordinate?(new_x, new_y) || event.through
return false if self != $game_player || event.character_name != ""
end
if $game_player.x == new_x && $game_player.y == new_y
return false if !$game_player.through && @character_name != ""
if $game_player.x == new_x && $game_player.y == new_y &&
!$game_player.through && @character_name != ""
return false
end
return true
end
@@ -243,24 +276,24 @@ class Game_Character
case dir
when 2, 8 # Down, up
y_diff = (dir == 8) ? @height - 1 : 0
for i in start_x...(start_x + @width)
(start_x...(start_x + @width)).each do |i|
return false if !passable?(i, start_y - y_diff, dir, strict)
end
return true
when 4, 6 # Left, right
x_diff = (dir == 6) ? @width - 1 : 0
for i in (start_y - @height + 1)..start_y
((start_y - @height + 1)..start_y).each do |i|
return false if !passable?(start_x + x_diff, i, dir, strict)
end
return true
when 1, 3 # Down diagonals
# Treated as moving down first and then horizontally, because that
# describes which tiles the character's feet touch
for i in start_x...(start_x + @width)
(start_x...(start_x + @width)).each do |i|
return false if !passable?(i, start_y, 2, strict)
end
x_diff = (dir == 3) ? @width - 1 : 0
for i in (start_y - @height + 1)..start_y
((start_y - @height + 1)..start_y).each do |i|
return false if !passable?(start_x + x_diff, i + 1, dir + 3, strict)
end
return true
@@ -268,12 +301,12 @@ class Game_Character
# Treated as moving horizontally first and then up, because that describes
# which tiles the character's feet touch
x_diff = (dir == 9) ? @width - 1 : 0
for i in (start_y - @height + 1)..start_y
((start_y - @height + 1)..start_y).each do |i|
return false if !passable?(start_x + x_diff, i, dir - 3, strict)
end
x_offset = (dir == 9) ? 1 : -1
for i in start_x...(start_x + @width)
return false if !passable?(i + x_offset, start_y - @height + 1, 8, strict)
x_tile_offset = (dir == 9) ? 1 : -1
(start_x...(start_x + @width)).each do |i|
return false if !passable?(i + x_tile_offset, start_y - @height + 1, 8, strict)
end
return true
end
@@ -284,17 +317,19 @@ class Game_Character
return can_move_from_coordinate?(@x, @y, dir, strict)
end
#=============================================================================
#-----------------------------------------------------------------------------
# Screen position of the character
#=============================================================================
#-----------------------------------------------------------------------------
def screen_x
ret = ((@real_x - self.map.display_x) / Game_Map::X_SUBPIXELS).round
ret = ((@real_x.to_f - self.map.display_x) / Game_Map::X_SUBPIXELS).round
ret += @width * Game_Map::TILE_WIDTH / 2
ret += self.x_offset
return ret
end
def screen_y_ground
ret = ((@real_y - self.map.display_y) / Game_Map::Y_SUBPIXELS).round
ret = ((@real_y.to_f - self.map.display_y) / Game_Map::Y_SUBPIXELS).round
ret += Game_Map::TILE_HEIGHT
return ret
end
@@ -302,13 +337,10 @@ class Game_Character
def screen_y
ret = screen_y_ground
if jumping?
if @jump_count > 0
jump_fraction = ((@jump_count * jump_speed_real / Game_Map::REAL_RES_X) - 0.5).abs # 0.5 to 0 to 0.5
else
jump_fraction = ((@jump_distance_left / @jump_distance) - 0.5).abs # 0.5 to 0 to 0.5
end
ret += @jump_peak * (4 * jump_fraction**2 - 1)
jump_progress = (@jump_fraction - 0.5).abs # 0.5 to 0 to 0.5
ret += @jump_peak * ((4 * (jump_progress**2)) - 1)
end
ret += self.y_offset
return ret
end
@@ -317,7 +349,7 @@ class Game_Character
z = screen_y_ground
if @tile_id > 0
begin
return z + self.map.priorities[@tile_id] * 32
return z + (self.map.priorities[@tile_id] * 32)
rescue
raise "Event's graphic is an out-of-range tile (event #{@id}, map #{self.map.map_id})"
end
@@ -326,16 +358,16 @@ class Game_Character
return z + ((height > Game_Map::TILE_HEIGHT) ? Game_Map::TILE_HEIGHT - 1 : 0)
end
#=============================================================================
#-----------------------------------------------------------------------------
# Movement
#=============================================================================
#-----------------------------------------------------------------------------
def moving?
return @real_x != @x * Game_Map::REAL_RES_X ||
@real_y != @y * Game_Map::REAL_RES_Y
return !@move_timer.nil?
end
def jumping?
return (@jump_distance_left || 0) > 0 || @jump_count > 0
return !@jump_timer.nil?
end
def straighten
@@ -345,7 +377,7 @@ class Game_Character
end
def force_move_route(move_route)
if @original_move_route == nil
if @original_move_route.nil?
@original_move_route = @move_route
@original_move_route_index = @move_route_index
end
@@ -354,6 +386,7 @@ class Game_Character
@move_route_forcing = true
@prelock_direction = 0
@wait_count = 0
@wait_start = nil
move_type_custom
end
@@ -363,14 +396,15 @@ class Game_Character
@real_x = @x * Game_Map::REAL_RES_X
@real_y = @y * Game_Map::REAL_RES_Y
@prelock_direction = 0
@moveto_happened = true
calculate_bush_depth
triggerLeaveTile
end
def triggerLeaveTile
if @oldX && @oldY && @oldMap &&
(@oldX!=self.x || @oldY!=self.y || @oldMap!=self.map.map_id)
Events.onLeaveTile.trigger(self,self,@oldMap,@oldX,@oldY)
(@oldX != self.x || @oldY != self.y || @oldMap != self.map.map_id)
EventHandlers.trigger(:on_leave_tile, self, @oldMap, @oldX, @oldY)
end
@oldX = self.x
@oldY = self.y
@@ -382,9 +416,10 @@ class Game_Character
triggerLeaveTile
end
#=============================================================================
#-----------------------------------------------------------------------------
# Movement commands
#=============================================================================
#-----------------------------------------------------------------------------
def move_type_random
case rand(6)
when 0..3 then move_random
@@ -394,8 +429,8 @@ class Game_Character
end
def move_type_toward_player
sx = @x + @width / 2.0 - ($game_player.x + $game_player.width / 2.0)
sy = @y - @height / 2.0 - ($game_player.y - $game_player.height / 2.0)
sx = @x + (@width / 2.0) - ($game_player.x + ($game_player.width / 2.0))
sy = @y - (@height / 2.0) - ($game_player.y - ($game_player.height / 2.0))
if sx.abs + sy.abs >= 20
move_random
return
@@ -409,11 +444,14 @@ class Game_Character
def move_type_custom
return if jumping? || moving?
while @move_route_index < @move_route.list.size
return if @move_route.list.size <= 1 # Empty move route
start_index = @move_route_index
(@move_route.list.size - 1).times do
command = @move_route.list[@move_route_index]
if command.code == 0
if @move_route.repeat
@move_route_index = 0
command = @move_route.list[@move_route_index]
else
if @move_route_forcing
@move_route_forcing = false
@@ -422,9 +460,12 @@ class Game_Character
@original_move_route = nil
end
@stop_count = 0
return
end
return
end
done_one_command = true
# The below move route commands wait for a frame (i.e. return) after
# executing them
if command.code <= 14
case command.code
when 1 then move_down
@@ -445,13 +486,13 @@ class Game_Character
@move_route_index += 1 if @move_route.skippable || moving? || jumping?
return
end
if command.code == 15 # Wait
@wait_count = (command.parameters[0] * Graphics.frame_rate / 20) - 1
@move_route_index += 1
return
end
if command.code >= 16 && command.code <= 26
# The below move route commands wait for a frame (i.e. return) after
# executing them
if command.code >= 15 && command.code <= 26
case command.code
when 15 # Wait
@wait_count = command.parameters[0] / 20.0
@wait_start = System.uptime
when 16 then turn_down
when 17 then turn_left
when 18 then turn_right
@@ -467,6 +508,8 @@ class Game_Character
@move_route_index += 1
return
end
# The below move route commands don't wait for a frame (i.e. return) after
# executing them
if command.code >= 27
case command.code
when 27
@@ -511,7 +554,14 @@ class Game_Character
when 42 then @opacity = command.parameters[0]
when 43 then @blend_type = command.parameters[0]
when 44 then pbSEPlay(command.parameters[0])
when 45 then eval(command.parameters[0])
when 45
eval(command.parameters[0])
if command.parameters[0][/^move_random_range/] ||
command.parameters[0][/^move_random_UD/] ||
command.parameters[0][/^move_random_LR/]
@move_route_index += 1
return
end
end
@move_route_index += 1
end
@@ -522,8 +572,11 @@ class Game_Character
turn_generic(dir) if turn_enabled
if can_move_in_direction?(dir)
turn_generic(dir)
@move_initial_x = @x
@move_initial_y = @y
@x += (dir == 4) ? -1 : (dir == 6) ? 1 : 0
@y += (dir == 8) ? -1 : (dir == 2) ? 1 : 0
@move_timer = 0.0
increase_steps
else
check_event_trigger_touch(dir)
@@ -551,8 +604,11 @@ class Game_Character
@direction = (@direction == 6 ? 4 : @direction == 2 ? 8 : @direction)
end
if can_move_in_direction?(7)
@move_initial_x = @x
@move_initial_y = @y
@x -= 1
@y -= 1
@move_timer = 0.0
increase_steps
end
end
@@ -562,8 +618,11 @@ class Game_Character
@direction = (@direction == 4 ? 6 : @direction == 2 ? 8 : @direction)
end
if can_move_in_direction?(9)
@move_initial_x = @x
@move_initial_y = @y
@x += 1
@y -= 1
@move_timer = 0.0
increase_steps
end
end
@@ -573,8 +632,11 @@ class Game_Character
@direction = (@direction == 6 ? 4 : @direction == 8 ? 2 : @direction)
end
if can_move_in_direction?(1)
@move_initial_x = @x
@move_initial_y = @y
@x -= 1
@y += 1
@move_timer = 0.0
increase_steps
end
end
@@ -584,13 +646,17 @@ class Game_Character
@direction = (@direction == 4 ? 6 : @direction == 8 ? 2 : @direction)
end
if can_move_in_direction?(3)
@move_initial_x = @x
@move_initial_y = @y
@x += 1
@y += 1
@move_timer = 0.0
increase_steps
end
end
def moveLeft90 # anticlockwise
# Anticlockwise.
def moveLeft90
case self.direction
when 2 then move_right # down
when 4 then move_down # left
@@ -599,7 +665,8 @@ class Game_Character
end
end
def moveRight90 # clockwise
# Clockwise.
def moveRight90
case self.direction
when 2 then move_left # down
when 4 then move_up # left
@@ -617,21 +684,23 @@ class Game_Character
end
end
def move_random_range(xrange=-1,yrange=-1)
def move_random_range(xrange = -1, yrange = -1)
dirs = [] # 0=down, 1=left, 2=right, 3=up
if xrange<0
dirs.push(1); dirs.push(2)
elsif xrange>0
if xrange < 0
dirs.push(1)
dirs.push(2)
elsif xrange > 0
dirs.push(1) if @x > @original_x - xrange
dirs.push(2) if @x < @original_x + xrange
end
if yrange<0
dirs.push(0); dirs.push(3)
elsif yrange>0
if yrange < 0
dirs.push(0)
dirs.push(3)
elsif yrange > 0
dirs.push(0) if @y < @original_y + yrange
dirs.push(3) if @y > @original_y - yrange
end
return if dirs.length==0
return if dirs.length == 0
case dirs[rand(dirs.length)]
when 0 then move_down(false)
when 1 then move_left(false)
@@ -640,17 +709,17 @@ class Game_Character
end
end
def move_random_UD(range=-1)
move_random_range(0,range)
def move_random_UD(range = -1)
move_random_range(0, range)
end
def move_random_LR(range=-1)
move_random_range(range,0)
def move_random_LR(range = -1)
move_random_range(range, 0)
end
def move_toward_player
sx = @x + @width / 2.0 - ($game_player.x + $game_player.width / 2.0)
sy = @y - @height / 2.0 - ($game_player.y - $game_player.height / 2.0)
sx = @x + (@width / 2.0) - ($game_player.x + ($game_player.width / 2.0))
sy = @y - (@height / 2.0) - ($game_player.y - ($game_player.height / 2.0))
return if sx == 0 && sy == 0
abs_sx = sx.abs
abs_sy = sy.abs
@@ -658,21 +727,29 @@ class Game_Character
(rand(2) == 0) ? abs_sx += 1 : abs_sy += 1
end
if abs_sx > abs_sy
(sx > 0) ? move_left : move_right
if abs_sx >= 1
(sx > 0) ? move_left : move_right
end
if !moving? && sy != 0
(sy > 0) ? move_up : move_down
if abs_sy >= 1
(sy > 0) ? move_up : move_down
end
end
else
(sy > 0) ? move_up : move_down
if abs_sy >= 1
(sy > 0) ? move_up : move_down
end
if !moving? && sx != 0
(sx > 0) ? move_left : move_right
if abs_sx >= 1
(sx > 0) ? move_left : move_right
end
end
end
end
def move_away_from_player
sx = @x + @width / 2.0 - ($game_player.x + $game_player.width / 2.0)
sy = @y - @height / 2.0 - ($game_player.y - $game_player.height / 2.0)
sx = @x + (@width / 2.0) - ($game_player.x + ($game_player.width / 2.0))
sy = @y - (@height / 2.0) - ($game_player.y - ($game_player.height / 2.0))
return if sx == 0 && sy == 0
abs_sx = sx.abs
abs_sy = sy.abs
@@ -722,42 +799,43 @@ class Game_Character
end
each_occupied_tile { |i, j| return if !passable?(i + x_plus, j + y_plus, 0) }
end
@x = @x + x_plus
@y = @y + y_plus
real_distance = Math::sqrt(x_plus * x_plus + y_plus * y_plus)
@jump_initial_x = @x
@jump_initial_y = @y
@x += x_plus
@y += y_plus
@jump_timer = 0.0
real_distance = Math.sqrt((x_plus**2) + (y_plus**2))
distance = [1, real_distance].max
@jump_peak = distance * Game_Map::TILE_HEIGHT * 3 / 8 # 3/4 of tile for ledge jumping
@jump_distance = [x_plus.abs * Game_Map::REAL_RES_X, y_plus.abs * Game_Map::REAL_RES_Y].max
@jump_distance_left = 1 # Just needs to be non-zero
if real_distance > 0 # Jumping to somewhere else
@jump_count = 0
else # Jumping on the spot
@jump_speed_real = nil # Reset jump speed
@jump_count = Game_Map::REAL_RES_X / jump_speed_real # Number of frames to jump one tile
end
@stop_count = 0
if self.is_a?(Game_Player)
$PokemonTemp.dependentEvents.pbMoveDependentEvents
end
triggerLeaveTile
@jumping_on_spot = (real_distance == 0)
increase_steps
end
def jumpForward
def jumpForward(distance = 1)
return false if distance == 0
old_x = @x
old_y = @y
case self.direction
when 2 then jump(0,1) # down
when 4 then jump(-1,0) # left
when 6 then jump(1,0) # right
when 8 then jump(0,-1) # up
when 2 then jump(0, distance) # down
when 4 then jump(-distance, 0) # left
when 6 then jump(distance, 0) # right
when 8 then jump(0, -distance) # up
end
return @x != old_x || @y != old_y
end
def jumpBackward
def jumpBackward(distance = 1)
return false if distance == 0
old_x = @x
old_y = @y
case self.direction
when 2 then jump(0,-1) # down
when 4 then jump(1,0) # left
when 6 then jump(-1,0) # right
when 8 then jump(0,1) # up
when 2 then jump(0, -distance) # down
when 4 then jump(distance, 0) # left
when 6 then jump(-distance, 0) # right
when 8 then jump(0, distance) # up
end
return @x != old_x || @y != old_y
end
def turn_generic(dir)
@@ -765,7 +843,7 @@ class Game_Character
oldDirection = @direction
@direction = dir
@stop_count = 0
pbCheckEventTriggerAfterTurning if dir != oldDirection
check_event_trigger_after_turning if dir != oldDirection
end
def turn_down; turn_generic(2); end
@@ -814,8 +892,8 @@ class Game_Character
end
def turn_toward_player
sx = @x + @width / 2.0 - ($game_player.x + $game_player.width / 2.0)
sy = @y - @height / 2.0 - ($game_player.y - $game_player.height / 2.0)
sx = @x + (@width / 2.0) - ($game_player.x + ($game_player.width / 2.0))
sy = @y - (@height / 2.0) - ($game_player.y - ($game_player.height / 2.0))
return if sx == 0 && sy == 0
if sx.abs > sy.abs
(sx > 0) ? turn_left : turn_right
@@ -825,8 +903,8 @@ class Game_Character
end
def turn_away_from_player
sx = @x + @width / 2.0 - ($game_player.x + $game_player.width / 2.0)
sy = @y - @height / 2.0 - ($game_player.y - $game_player.height / 2.0)
sx = @x + (@width / 2.0) - ($game_player.x + ($game_player.width / 2.0))
sy = @y - (@height / 2.0) - ($game_player.y - ($game_player.height / 2.0))
return if sx == 0 && sy == 0
if sx.abs > sy.abs
(sx > 0) ? turn_right : turn_left
@@ -835,26 +913,36 @@ class Game_Character
end
end
#=============================================================================
#-----------------------------------------------------------------------------
# Updating
#=============================================================================
#-----------------------------------------------------------------------------
def update
return if $game_temp.in_menu
time_now = System.uptime
@last_update_time = time_now if !@last_update_time || @last_update_time > time_now
@delta_t = time_now - @last_update_time
@last_update_time = time_now
return if @delta_t > 0.25 # Was in a menu; delay movement
@moved_last_frame = @moved_this_frame
@stopped_last_frame = @stopped_this_frame
if !$game_temp.in_menu
# Update command
update_command
# Update movement
(moving? || jumping?) ? update_move : update_stop
end
@moved_this_frame = false
@stopped_this_frame = false
# Update command
update_command
# Update movement
(moving? || jumping?) ? update_move : update_stop
# Update animation
update_pattern
end
def update_command
if @wait_count > 0
@wait_count -= 1
elsif @move_route_forcing
return if System.uptime - @wait_start < @wait_count
@wait_count = 0
@wait_start = nil
end
if @move_route_forcing
move_type_custom
elsif !@starting && !lock? && !moving? && !jumping?
update_command_new
@@ -862,15 +950,7 @@ class Game_Character
end
def update_command_new
# @stop_count is the number of frames since the last movement finished.
# @move_frequency has these values:
# 1 => @stop_count > 190 # 4.75 seconds
# 2 => @stop_count > 144 # 3.6 seconds
# 3 => @stop_count > 102 # 2.55 seconds
# 4 => @stop_count > 64 # 1.6 seconds
# 5 => @stop_count > 30 # 0.75 seconds
# 6 => @stop_count > 0 # 0 seconds
if @stop_count >= self.move_frequency_real
if @stop_count >= @command_delay
case @move_type
when 1 then move_type_random
when 2 then move_type_toward_player
@@ -880,53 +960,71 @@ class Game_Character
end
def update_move
# Move the character (the 0.1 catches rounding errors)
distance = (jumping?) ? jump_speed_real : move_speed_real
dest_x = @x * Game_Map::REAL_RES_X
dest_y = @y * Game_Map::REAL_RES_Y
if @real_x < dest_x
@real_x += distance
@real_x = dest_x if @real_x > dest_x - 0.1
else
@real_x -= distance
@real_x = dest_x if @real_x < dest_x + 0.1
if @move_timer
@move_timer += @delta_t
# Move horizontally
if @x != @move_initial_x
dist = (@move_initial_x - @x).abs
@real_x = lerp(@move_initial_x, @x, @move_time * dist, @move_timer) * Game_Map::REAL_RES_X
end
# Move vertically
if @y != @move_initial_y
dist = (@move_initial_y - @y).abs
@real_y = lerp(@move_initial_y, @y, @move_time * dist, @move_timer) * Game_Map::REAL_RES_Y
end
elsif @jump_timer
self.jump_speed = 3 if !@jump_time
@jump_timer += @delta_t
dist = [(@x - @jump_initial_x).abs, (@y - @jump_initial_y).abs].max
dist = 1 if dist == 0 # Jumping on spot
# Move horizontally
if @x != @jump_initial_x
@real_x = lerp(@jump_initial_x, @x, @jump_time * dist, @jump_timer) * Game_Map::REAL_RES_X
end
# Move vertically
if @y != @jump_initial_y
@real_y = lerp(@jump_initial_y, @y, @jump_time * dist, @jump_timer) * Game_Map::REAL_RES_Y
end
# Calculate how far through the jump we are (from 0 to 1)
@jump_fraction = @jump_timer / (@jump_time * dist)
end
if @real_y < dest_y
@real_y += distance
@real_y = dest_y if @real_y > dest_y - 0.1
else
@real_y -= distance
@real_y = dest_y if @real_y < dest_y + 0.1
# Snap to end position if close enough
@real_x = @x * Game_Map::REAL_RES_X if (@real_x - (@x * Game_Map::REAL_RES_X)).abs < Game_Map::X_SUBPIXELS / 2
@real_y = @y * Game_Map::REAL_RES_Y if (@real_y - (@y * Game_Map::REAL_RES_Y)).abs < Game_Map::Y_SUBPIXELS / 2
# End of move
if moving? && @move_timer >= @move_time &&
@real_x == @x * Game_Map::REAL_RES_X && @real_y == @y * Game_Map::REAL_RES_Y
@move_timer = nil
end
# Refresh how far is left to travel in a jump
if jumping?
@jump_count -= 1 if @jump_count > 0 # For stationary jumps only
@jump_distance_left = [(dest_x - @real_x).abs, (dest_y - @real_y).abs].max
# End of jump
if jumping? && @jump_fraction >= 1
@jump_timer = nil
@jump_peak = 0
@jump_distance = 0
@jump_fraction = 0
@jumping_on_spot = false
end
# End of a step, so perform events that happen at this time
if !jumping? && !moving?
Events.onStepTakenFieldMovement.trigger(self, self)
EventHandlers.trigger(:on_step_taken, self)
calculate_bush_depth
@stopped_this_frame = true
elsif !@moved_last_frame || @stopped_last_frame # Started a new step
calculate_bush_depth
@stopped_this_frame = false
end
# Increment animation counter
@anime_count += 1 if @walk_anime || @step_anime
@anime_count += @delta_t if @walk_anime || @step_anime
@moved_this_frame = true
end
def update_stop
@anime_count += 1 if @step_anime
@stop_count += 1 if !@starting && !lock?
@moved_this_frame = false
@stopped_this_frame = false
@anime_count += @delta_t if @step_anime
@stop_count += @delta_t if !@starting && !lock?
end
def update_pattern
return if @lock_pattern
# return if @jump_count > 0 # Don't animate if jumping on the spot
# return if @jumping_on_spot # Don't animate if jumping on the spot
# Character has stopped moving, return to original pattern
if @moved_last_frame && !@moved_this_frame && !@step_anime
@pattern = @original_pattern
@@ -942,12 +1040,10 @@ class Game_Character
# Calculate how many frames each pattern should display for, i.e. the time
# it takes to move half a tile (or a whole tile if cycling). We assume the
# game uses square tiles.
real_speed = (jumping?) ? jump_speed_real : move_speed_real
frames_per_pattern = Game_Map::REAL_RES_X / (real_speed * 2.0)
frames_per_pattern *= 2 if move_speed >= 5 # Cycling speed or faster
return if @anime_count < frames_per_pattern
pattern_time = pattern_update_speed / 4 # 4 frames per cycle in a charset
return if @anime_count < pattern_time
# Advance to the next animation frame
@pattern = (@pattern + 1) % 4
@anime_count -= frames_per_pattern
@anime_count -= pattern_time
end
end

View File

@@ -1,3 +1,6 @@
#===============================================================================
#
#===============================================================================
class Game_Event < Game_Character
attr_reader :map_id
attr_reader :trigger
@@ -6,7 +9,7 @@ class Game_Event < Game_Character
attr_reader :tempSwitches # Temporary self-switches
attr_accessor :need_refresh
def initialize(map_id, event, map=nil)
def initialize(map_id, event, map = nil)
super(map)
@map_id = map_id
@event = event
@@ -54,7 +57,7 @@ class Game_Event < Game_Character
end
def tsOn?(c)
return @tempSwitches && @tempSwitches[c]==true
return @tempSwitches && @tempSwitches[c] == true
end
def tsOff?(c)
@@ -62,56 +65,62 @@ class Game_Event < Game_Character
end
def setTempSwitchOn(c)
@tempSwitches[c]=true
@tempSwitches[c] = true
refresh
end
def setTempSwitchOff(c)
@tempSwitches[c]=false
@tempSwitches[c] = false
refresh
end
def isOff?(c)
return !$game_self_switches[[@map_id,@event.id,c]]
return !$game_self_switches[[@map_id, @event.id, c]]
end
def switchIsOn?(id)
switchname = $data_system.switches[id]
return false if !switchname
if switchname[/^s\:/]
switch_name = $data_system.switches[id]
if switch_name && switch_name[/^s\:/]
return eval($~.post_match)
else
return $game_switches[id]
end
return $game_switches[id]
end
def variableIsLessThan?(id, value)
variable_name = $data_system.variables[id]
if variable_name && variable_name[/^s\:/]
return eval($~.post_match) < value
end
return $game_variables[id] < value
end
def variable
return nil if !$PokemonGlobal.eventvars
return $PokemonGlobal.eventvars[[@map_id,@event.id]]
return $PokemonGlobal.eventvars[[@map_id, @event.id]]
end
def setVariable(variable)
$PokemonGlobal.eventvars[[@map_id,@event.id]]=variable
$PokemonGlobal.eventvars[[@map_id, @event.id]] = variable
end
def varAsInt
return 0 if !$PokemonGlobal.eventvars
return $PokemonGlobal.eventvars[[@map_id,@event.id]].to_i
return $PokemonGlobal.eventvars[[@map_id, @event.id]].to_i
end
def expired?(secs=86400)
ontime=self.variable
time=pbGetTimeNow
return ontime && (time.to_i>ontime+secs)
def expired?(secs = 86_400)
ontime = self.variable
time = pbGetTimeNow
return ontime && (time.to_i > ontime + secs)
end
def expiredDays?(days=1)
ontime=self.variable.to_i
def expiredDays?(days = 1)
ontime = self.variable.to_i
return false if !ontime
now=pbGetTimeNow
elapsed=(now.to_i-ontime)/86400
elapsed+=1 if (now.to_i-ontime)%86400>(now.hour*3600+now.min*60+now.sec)
return elapsed>=days
now = pbGetTimeNow
elapsed = (now.to_i - ontime) / 86_400
elapsed += 1 if (now.to_i - ontime) % 86_400 > ((now.hour * 3600) + (now.min * 60) + now.sec)
return elapsed >= days
end
def cooledDown?(seconds)
@@ -127,10 +136,11 @@ class Game_Event < Game_Character
end
def onEvent?
return @map_id == $game_map.map_id && at_coordinate?($game_player.x, $game_player.y)
return @map_id == $game_player.map_id && at_coordinate?($game_player.x, $game_player.y)
end
def over_trigger?
return false if @map_id != $game_player.map_id
return false if @character_name != "" && !@through
return false if @event.name[/hiddenitem/i]
each_occupied_tile do |i, j|
@@ -139,19 +149,10 @@ class Game_Event < Game_Character
return false
end
def pbCheckEventTriggerAfterTurning
return if $game_system.map_interpreter.running? || @starting
if @event.name[/trainer\((\d+)\)/i]
distance = $~[1].to_i
if @trigger==2 && pbEventCanReachPlayer?(self,$game_player,distance)
start if !jumping? && !over_trigger?
end
end
end
def check_event_trigger_touch(dir)
return if $game_system.map_interpreter.running?
return if @map_id != $game_player.map_id
return if @trigger != 2 # Event touch
return if $game_system.map_interpreter.running?
case dir
when 2
return if $game_player.y != @y + 1
@@ -167,12 +168,41 @@ class Game_Event < Game_Character
start
end
def check_event_trigger_after_turning
return if @map_id != $game_player.map_id
return if @trigger != 2 # Not Event Touch
return if $game_system.map_interpreter.running? || @starting
return if !self.name[/(?:sight|trainer)\((\d+)\)/i]
distance = $~[1].to_i
return if !pbEventCanReachPlayer?(self, $game_player, distance)
return if jumping? || over_trigger?
start
end
def check_event_trigger_after_moving
return if @map_id != $game_player.map_id
return if @trigger != 2 # Not Event Touch
return if $game_system.map_interpreter.running? || @starting
if self.name[/(?:sight|trainer)\((\d+)\)/i]
distance = $~[1].to_i
return if !pbEventCanReachPlayer?(self, $game_player, distance)
elsif self.name[/counter\((\d+)\)/i]
distance = $~[1].to_i
return if !pbEventFacesPlayer?(self, $game_player, distance)
else
return
end
return if jumping? || over_trigger?
start
end
def check_event_trigger_auto
if @trigger == 2 # Event touch
if at_coordinate?($game_player.x, $game_player.y)
start if !jumping? && over_trigger?
case @trigger
when 2 # Event touch
if at_coordinate?($game_player.x, $game_player.y) && !jumping? && over_trigger?
start
end
elsif @trigger == 3 # Autorun
when 3 # Autorun
start
end
end
@@ -180,11 +210,11 @@ class Game_Event < Game_Character
def refresh
new_page = nil
unless @erased
for page in @event.pages.reverse
@event.pages.reverse.each do |page|
c = page.condition
next if c.switch1_valid && !switchIsOn?(c.switch1_id)
next if c.switch2_valid && !switchIsOn?(c.switch2_id)
next if c.variable_valid && $game_variables[c.variable_id] < c.variable_value
next if c.variable_valid && variableIsLessThan?(c.variable_id, c.variable_value)
if c.self_switch_valid
key = [@map_id, @event.id, c.self_switch_ch]
next if $game_self_switches[key] != true
@@ -196,7 +226,7 @@ class Game_Event < Game_Character
return if new_page == @page
@page = new_page
clear_starting
if @page == nil
if @page.nil?
@tile_id = 0
@character_name = ""
@character_hue = 0
@@ -236,43 +266,50 @@ class Game_Event < Game_Character
@trigger = @page.trigger
@list = @page.list
@interpreter = nil
if @trigger == 4 # Parallel Process
@interpreter = Interpreter.new
end
@interpreter = Interpreter.new if @trigger == 4 # Parallel Process
check_event_trigger_auto
end
def should_update?(recalc=false)
def should_update?(recalc = false)
return @to_update if !recalc
return true if @updated_last_frame
return true if @trigger && (@trigger == 3 || @trigger == 4)
return true if @move_route_forcing
return true if @move_route_forcing || @moveto_happened
return true if @event.name[/update/i]
range = 2 # Number of tiles
return false if self.screen_x - @sprite_size[0]/2 > Graphics.width + range * Game_Map::TILE_WIDTH
return false if self.screen_x + @sprite_size[0]/2 < -range * Game_Map::TILE_WIDTH
return false if self.screen_y_ground - @sprite_size[1] > Graphics.height + range * Game_Map::TILE_HEIGHT
return false if self.screen_x - (@sprite_size[0] / 2) > Graphics.width + (range * Game_Map::TILE_WIDTH)
return false if self.screen_x + (@sprite_size[0] / 2) < -range * Game_Map::TILE_WIDTH
return false if self.screen_y_ground - @sprite_size[1] > Graphics.height + (range * Game_Map::TILE_HEIGHT)
return false if self.screen_y_ground < -range * Game_Map::TILE_HEIGHT
return true
end
def update
@to_update = should_update?(true)
@updated_last_frame = false
return if !@to_update
@updated_last_frame = true
@moveto_happened = false
last_moving = moving?
super
if !moving? && last_moving
$game_player.pbCheckEventTriggerFromDistance([2])
end
check_event_trigger_after_moving if !moving? && last_moving
if @need_refresh
@need_refresh = false
refresh
end
check_event_trigger_auto
if @interpreter != nil
unless @interpreter.running?
@interpreter.setup(@list, @event.id, @map_id)
end
if @interpreter
@interpreter.setup(@list, @event.id, @map_id) if !@interpreter.running?
@interpreter.update
end
end
def update_move
was_jumping = jumping?
super
if was_jumping && !jumping? && !@transparent && (@tile_id > 0 || @character_name != "")
spriteset = $scene.spriteset(map_id)
spriteset&.addUserAnimation(Settings::DUST_ANIMATION_ID, self.x, self.y, true, 1)
end
end
end

View File

@@ -0,0 +1,616 @@
#===============================================================================
# ** Game_Player
#-------------------------------------------------------------------------------
# This class handles the player. Its functions include event starting
# determinants and map scrolling. Refer to "$game_player" for the one
# instance of this class.
#===============================================================================
class Game_Player < Game_Character
attr_accessor :charsetData
attr_accessor :encounter_count
SCREEN_CENTER_X = ((Settings::SCREEN_WIDTH / 2) - (Game_Map::TILE_WIDTH / 2)) * Game_Map::X_SUBPIXELS
SCREEN_CENTER_Y = ((Settings::SCREEN_HEIGHT / 2) - (Game_Map::TILE_HEIGHT / 2)) * Game_Map::Y_SUBPIXELS
# Time in seconds for one cycle of bobbing (playing 4 charset frames) while
# surfing or diving.
SURF_BOB_DURATION = 1.5
def initialize(*arg)
super(*arg)
@lastdir = 0
@lastdirframe = 0
end
def map
@map = nil
return $game_map
end
def map_id
return $game_map.map_id
end
def screen_z(height = 0)
ret = super
return ret + 1
end
def has_follower?
return $PokemonGlobal.followers.length > 0
end
def can_map_transfer_with_follower?
return $PokemonGlobal.followers.length == 0
end
def can_ride_vehicle_with_follower?
return $PokemonGlobal.followers.length == 0
end
#-----------------------------------------------------------------------------
def can_run?
return @move_speed > 3 if @move_route_forcing
return false if $game_temp.in_menu || $game_temp.in_battle ||
$game_temp.message_window_showing || pbMapInterpreterRunning?
return false if !$player.has_running_shoes && !$PokemonGlobal.diving &&
!$PokemonGlobal.surfing && !$PokemonGlobal.bicycle
return false if jumping?
return false if pbTerrainTag.must_walk
return ($PokemonSystem.runstyle == 1) ^ Input.press?(Input::BACK)
end
def set_movement_type(type)
meta = GameData::PlayerMetadata.get($player&.character_ID || 1)
new_charset = nil
case type
when :fishing
new_charset = pbGetPlayerCharset(meta.fish_charset)
when :surf_fishing
new_charset = pbGetPlayerCharset(meta.surf_fish_charset)
when :diving, :diving_fast, :diving_jumping, :diving_stopped
self.move_speed = 3 if !@move_route_forcing
new_charset = pbGetPlayerCharset(meta.dive_charset)
when :surfing, :surfing_fast, :surfing_jumping, :surfing_stopped
if !@move_route_forcing
self.move_speed = (type == :surfing_jumping) ? 3 : 4
end
new_charset = pbGetPlayerCharset(meta.surf_charset)
when :descending_waterfall, :ascending_waterfall
self.move_speed = 2 if !@move_route_forcing
new_charset = pbGetPlayerCharset(meta.surf_charset)
when :cycling, :cycling_fast, :cycling_jumping, :cycling_stopped
if !@move_route_forcing
self.move_speed = (type == :cycling_jumping) ? 3 : 5
end
new_charset = pbGetPlayerCharset(meta.cycle_charset)
when :running
self.move_speed = 4 if !@move_route_forcing
new_charset = pbGetPlayerCharset(meta.run_charset)
when :ice_sliding
self.move_speed = 4 if !@move_route_forcing
new_charset = pbGetPlayerCharset(meta.walk_charset)
else # :walking, :jumping, :walking_stopped
self.move_speed = 3 if !@move_route_forcing
new_charset = pbGetPlayerCharset(meta.walk_charset)
end
@character_name = new_charset if new_charset
end
# Called when the player's character or outfit changes. Assumes the player
# isn't moving.
def refresh_charset
meta = GameData::PlayerMetadata.get($player&.character_ID || 1)
new_charset = nil
if $PokemonGlobal&.diving
new_charset = pbGetPlayerCharset(meta.dive_charset)
elsif $PokemonGlobal&.surfing
new_charset = pbGetPlayerCharset(meta.surf_charset)
elsif $PokemonGlobal&.bicycle
new_charset = pbGetPlayerCharset(meta.cycle_charset)
else
new_charset = pbGetPlayerCharset(meta.walk_charset)
end
@character_name = new_charset if new_charset
end
#-----------------------------------------------------------------------------
def bump_into_object
return if @bump_time_start && (System.uptime - @bump_time_start < @move_time)
pbSEPlay("Overrides bump") if !@move_route_forcing
$stats.bump_count += 1
@bump_time_start = System.uptime
end
def add_move_distance_to_stats(distance = 1)
if $PokemonGlobal&.diving || $PokemonGlobal&.surfing
$stats.distance_surfed += distance
elsif $PokemonGlobal&.bicycle
$stats.distance_cycled += distance
else
$stats.distance_walked += distance
end
$stats.distance_slid_on_ice += distance if $PokemonGlobal.ice_sliding
end
def move_generic(dir, turn_enabled = true)
turn_generic(dir, true) if turn_enabled
if !$game_temp.encounter_triggered
if can_move_in_direction?(dir)
x_offset = (dir == 4) ? -1 : (dir == 6) ? 1 : 0
y_offset = (dir == 8) ? -1 : (dir == 2) ? 1 : 0
# Jump over ledges
if pbFacingTerrainTag.ledge
if jumpForward(2)
pbSEPlay("Overrides jump")
increase_steps
end
return
elsif pbFacingTerrainTag.waterfall_crest && dir == 2
$PokemonGlobal.descending_waterfall = true
$game_player.through = true
$stats.waterfalls_descended += 1
end
# Jumping out of surfing back onto land
return if pbEndSurf(x_offset, y_offset)
# General movement
turn_generic(dir, true)
if !$game_temp.encounter_triggered
@move_initial_x = @x
@move_initial_y = @y
@x += x_offset
@y += y_offset
@move_timer = 0.0
add_move_distance_to_stats(x_offset.abs + y_offset.abs)
increase_steps
end
elsif !check_event_trigger_touch(dir)
bump_into_object
end
end
$game_temp.encounter_triggered = false
end
def turn_generic(dir, keep_enc_indicator = false)
old_direction = @direction
super(dir)
if @direction != old_direction && !@move_route_forcing && !pbMapInterpreterRunning?
EventHandlers.trigger(:on_player_change_direction)
$game_temp.encounter_triggered = false if !keep_enc_indicator
end
end
def jump(x_plus, y_plus)
old_x = @x
old_y = @y
super
add_move_distance_to_stats(x_plus.abs + y_plus.abs) if @x != old_x || @y != old_y
end
#-----------------------------------------------------------------------------
def pbTerrainTag(countBridge = false)
return $map_factory.getTerrainTagFromCoords(self.map.map_id, @x, @y, countBridge) if $map_factory
return $game_map.terrain_tag(@x, @y, countBridge)
end
def pbFacingEvent(ignoreInterpreter = false)
return nil if $game_system.map_interpreter.running? && !ignoreInterpreter
# Check the tile in front of the player for events
new_x = @x + (@direction == 6 ? 1 : @direction == 4 ? -1 : 0)
new_y = @y + (@direction == 2 ? 1 : @direction == 8 ? -1 : 0)
return nil if !$game_map.valid?(new_x, new_y)
$game_map.events.each_value do |event|
next if !event.at_coordinate?(new_x, new_y)
next if event.jumping? || event.over_trigger?
return event
end
# If the tile in front is a counter, check one tile beyond that for events
if $game_map.counter?(new_x, new_y)
new_x += (@direction == 6 ? 1 : @direction == 4 ? -1 : 0)
new_y += (@direction == 2 ? 1 : @direction == 8 ? -1 : 0)
$game_map.events.each_value do |event|
next if !event.at_coordinate?(new_x, new_y)
next if event.jumping? || event.over_trigger?
return event
end
end
return nil
end
def pbFacingTerrainTag(dir = nil)
dir = self.direction if !dir
return $map_factory.getFacingTerrainTag(dir, self) if $map_factory
facing = pbFacingTile(dir, self)
return $game_map.terrain_tag(facing[1], facing[2])
end
# Passable Determinants
# x : x-coordinate
# y : y-coordinate
# d : direction (0, 2, 4, 6, 8)
# * 0 = Determines if all directions are impassable (for jumping)
def passable?(x, y, dir, strict = false)
# Get new coordinates
new_x = x + (dir == 6 ? 1 : dir == 4 ? -1 : 0)
new_y = y + (dir == 2 ? 1 : dir == 8 ? -1 : 0)
# If coordinates are outside of map
return false if !$game_map.validLax?(new_x, new_y)
if !$game_map.valid?(new_x, new_y)
return false if !$map_factory
return $map_factory.isPassableFromEdge?(new_x, new_y, 10 - dir)
end
# If debug mode is ON and Ctrl key was pressed
return true if $DEBUG && Input.press?(Input::CTRL)
return super
end
# Set Map Display Position to Center of Screen
def center(x, y)
self.map.display_x = (x * Game_Map::REAL_RES_X) - SCREEN_CENTER_X
self.map.display_y = (y * Game_Map::REAL_RES_Y) - SCREEN_CENTER_Y
end
# Move to Designated Position
# x : x-coordinate
# y : y-coordinate
def moveto(x, y)
super
center(x, y)
make_encounter_count
end
# Make Encounter Count
def make_encounter_count
# Image of two dice rolling
if $game_map.map_id != 0
n = $game_map.encounter_step
@encounter_count = rand(n) + rand(n) + 1
end
end
def refresh
@opacity = 255
@blend_type = 0
end
#-----------------------------------------------------------------------------
def pbTriggeredTrainerEvents(triggers, checkIfRunning = true, trainer_only = false)
result = []
# If event is running
return result if checkIfRunning && $game_system.map_interpreter.running?
# All event loops
$game_map.events.each_value do |event|
next if !triggers.include?(event.trigger)
next if !event.name[/trainer\((\d+)\)/i] && (trainer_only || !event.name[/sight\((\d+)\)/i])
distance = $~[1].to_i
next if !pbEventCanReachPlayer?(event, self, distance)
next if event.jumping? || event.over_trigger?
result.push(event)
end
return result
end
def pbTriggeredCounterEvents(triggers, checkIfRunning = true)
result = []
# If event is running
return result if checkIfRunning && $game_system.map_interpreter.running?
# All event loops
$game_map.events.each_value do |event|
next if !triggers.include?(event.trigger)
next if !event.name[/counter\((\d+)\)/i]
distance = $~[1].to_i
next if !pbEventFacesPlayer?(event, self, distance)
next if event.jumping? || event.over_trigger?
result.push(event)
end
return result
end
def check_event_trigger_after_turning; end
def pbCheckEventTriggerFromDistance(triggers)
events = pbTriggeredTrainerEvents(triggers)
events.concat(pbTriggeredCounterEvents(triggers))
return false if events.length == 0
ret = false
events.each do |event|
event.start
ret = true if event.starting
end
return ret
end
# Trigger event(s) at the same coordinates as self with the appropriate
# trigger(s) that can be triggered
def check_event_trigger_here(triggers)
result = false
# If event is running
return result if $game_system.map_interpreter.running?
# All event loops
$game_map.events.each_value do |event|
# If event coordinates and triggers are consistent
next if !event.at_coordinate?(@x, @y)
next if !triggers.include?(event.trigger)
# If starting determinant is same position event (other than jumping)
next if event.jumping? || !event.over_trigger?
event.start
result = true if event.starting
end
return result
end
# Front Event Starting Determinant
def check_event_trigger_there(triggers)
result = false
# If event is running
return result if $game_system.map_interpreter.running?
# Calculate front event coordinates
new_x = @x + (@direction == 6 ? 1 : @direction == 4 ? -1 : 0)
new_y = @y + (@direction == 2 ? 1 : @direction == 8 ? -1 : 0)
return false if !$game_map.valid?(new_x, new_y)
# All event loops
$game_map.events.each_value do |event|
next if !triggers.include?(event.trigger)
# If event coordinates and triggers are consistent
next if !event.at_coordinate?(new_x, new_y)
# If starting determinant is front event (other than jumping)
next if event.jumping? || event.over_trigger?
event.start
result = true if event.starting
end
# If fitting event is not found
if result == false && $game_map.counter?(new_x, new_y)
# Calculate coordinates of 1 tile further away
new_x += (@direction == 6 ? 1 : @direction == 4 ? -1 : 0)
new_y += (@direction == 2 ? 1 : @direction == 8 ? -1 : 0)
return false if !$game_map.valid?(new_x, new_y)
# All event loops
$game_map.events.each_value do |event|
next if !triggers.include?(event.trigger)
# If event coordinates and triggers are consistent
next if !event.at_coordinate?(new_x, new_y)
# If starting determinant is front event (other than jumping)
next if event.jumping? || event.over_trigger?
event.start
result = true if event.starting
end
end
return result
end
# Touch Event Starting Determinant
def check_event_trigger_touch(dir)
result = false
return result if $game_system.map_interpreter.running?
# All event loops
x_offset = (dir == 4) ? -1 : (dir == 6) ? 1 : 0
y_offset = (dir == 8) ? -1 : (dir == 2) ? 1 : 0
$game_map.events.each_value do |event|
next if ![1, 2].include?(event.trigger) # Overrides touch, event touch
# If event coordinates and triggers are consistent
next if !event.at_coordinate?(@x + x_offset, @y + y_offset)
if event.name[/(?:sight|trainer)\((\d+)\)/i]
distance = $~[1].to_i
next if !pbEventCanReachPlayer?(event, self, distance)
elsif event.name[/counter\((\d+)\)/i]
distance = $~[1].to_i
next if !pbEventFacesPlayer?(event, self, distance)
end
# If starting determinant is front event (other than jumping)
next if event.jumping? || event.over_trigger?
event.start
result = true if event.starting
end
return result
end
#-----------------------------------------------------------------------------
def update
last_real_x = @real_x
last_real_y = @real_y
@last_terrain_tag = pbTerrainTag
super
update_stop if $game_temp.in_menu && @stopped_last_frame
update_screen_position(last_real_x, last_real_y)
# Update dependent events
if (!@moved_last_frame || @stopped_last_frame) && (moving? || jumping?)
$game_temp.followers.move_followers
end
$game_temp.followers.update
update_event_triggering
end
def update_command_new
dir = Input.dir4
if $PokemonGlobal.forced_movement?
move_forward
@last_input_time = nil
return
elsif dir <= 0
@last_input_time = nil
return
end
return if pbMapInterpreterRunning? || $game_temp.message_window_showing ||
$game_temp.in_mini_update || $game_temp.in_menu
# Move player in the direction the directional button is being pressed
if @moved_last_frame ||
(dir == direction && (!@last_input_time || System.uptime - @last_input_time >= 0.075))
case dir
when 2 then move_down
when 4 then move_left
when 6 then move_right
when 8 then move_up
end
@last_input_time = nil
elsif dir != direction
case dir
when 2 then turn_down
when 4 then turn_left
when 6 then turn_right
when 8 then turn_up
end
@last_input_time = System.uptime
end
end
def update_move
if !@moved_last_frame || @stopped_last_frame # Started a new step
if $PokemonGlobal.ice_sliding || @last_terrain_tag.ice
set_movement_type(:ice_sliding)
elsif $PokemonGlobal.descending_waterfall
set_movement_type(:descending_waterfall)
elsif $PokemonGlobal.ascending_waterfall
set_movement_type(:ascending_waterfall)
else
faster = can_run?
if $PokemonGlobal&.diving
set_movement_type((faster) ? :diving_fast : :diving)
elsif $PokemonGlobal&.surfing
set_movement_type((faster) ? :surfing_fast : :surfing)
elsif $PokemonGlobal&.bicycle
set_movement_type((faster) ? :cycling_fast : :cycling)
else
set_movement_type((faster) ? :running : :walking)
end
end
if jumping?
if $PokemonGlobal&.diving
set_movement_type(:diving_jumping)
elsif $PokemonGlobal&.surfing
set_movement_type(:surfing_jumping)
elsif $PokemonGlobal&.bicycle
set_movement_type(:cycling_jumping)
else
set_movement_type(:jumping) # Walking speed/charset while jumping
end
end
end
was_jumping = jumping?
super
if was_jumping && !jumping? && !@transparent && (@tile_id > 0 || @character_name != "")
if !$PokemonGlobal.surfing || $game_temp.ending_surf
spriteset = $scene.spriteset(map_id)
spriteset&.addUserAnimation(Settings::DUST_ANIMATION_ID, self.x, self.y, true, 1)
end
end
end
def update_stop
if @stopped_last_frame
if $PokemonGlobal&.diving
set_movement_type(:diving_stopped)
elsif $PokemonGlobal&.surfing
set_movement_type(:surfing_stopped)
elsif $PokemonGlobal&.bicycle
set_movement_type(:cycling_stopped)
else
set_movement_type(:walking_stopped)
end
end
super
end
def update_pattern
if $PokemonGlobal&.surfing || $PokemonGlobal&.diving
bob_pattern = (4 * System.uptime / SURF_BOB_DURATION).to_i % 4
@pattern = bob_pattern if !@lock_pattern
@pattern_surf = bob_pattern
@bob_height = (bob_pattern >= 2) ? 2 : 0
@anime_count = 0
else
@bob_height = 0
super
end
end
# Track the player on-screen as they move.
def update_screen_position(last_real_x, last_real_y)
return if self.map.scrolling? || !(@moved_last_frame || @moved_this_frame)
if (@real_x < last_real_x && @real_x < $game_map.display_x + SCREEN_CENTER_X) ||
(@real_x > last_real_x && @real_x > $game_map.display_x + SCREEN_CENTER_X)
self.map.display_x += @real_x - last_real_x
end
if (@real_y < last_real_y && @real_y < $game_map.display_y + SCREEN_CENTER_Y) ||
(@real_y > last_real_y && @real_y > $game_map.display_y + SCREEN_CENTER_Y)
self.map.display_y += @real_y - last_real_y
end
end
def update_event_triggering
return if moving? || jumping? || $PokemonGlobal.forced_movement?
# Try triggering events upon walking into them/in front of them
if @moved_this_frame
$game_temp.followers.turn_followers
result = pbCheckEventTriggerFromDistance([2])
# Event determinant is via touch of same position event
result |= check_event_trigger_here([1, 2])
# No events triggered, try other event triggers upon finishing a step
pbOnStepTaken(result)
end
end
end
#===============================================================================
#
#===============================================================================
def pbGetPlayerCharset(charset, trainer = nil, force = false)
trainer = $player if !trainer
outfit = (trainer) ? trainer.outfit : 0
return nil if !force && $game_player&.charsetData &&
$game_player.charsetData[0] == trainer.character_ID &&
$game_player.charsetData[1] == charset &&
$game_player.charsetData[2] == outfit
$game_player.charsetData = [trainer.character_ID, charset, outfit] if $game_player
ret = charset
if pbResolveBitmap("Graphics/Characters/" + ret + "_" + outfit.to_s)
ret = ret + "_" + outfit.to_s
end
return ret
end
def pbUpdateVehicle
if $PokemonGlobal&.diving
$game_player.set_movement_type(:diving_stopped)
elsif $PokemonGlobal&.surfing
$game_player.set_movement_type(:surfing_stopped)
elsif $PokemonGlobal&.bicycle
$game_player.set_movement_type(:cycling_stopped)
else
$game_player.set_movement_type(:walking_stopped)
end
end
def pbCancelVehicles(destination = nil, cancel_swimming = true)
$PokemonGlobal.surfing = false if cancel_swimming
$PokemonGlobal.diving = false if cancel_swimming
$PokemonGlobal.bicycle = false if !destination || !pbCanUseBike?(destination)
pbUpdateVehicle
end
def pbCanUseBike?(map_id)
map_metadata = GameData::MapMetadata.try_get(map_id)
return false if !map_metadata
return map_metadata.always_bicycle || map_metadata.can_bicycle || map_metadata.outdoor_map
end
def pbMountBike
return if $PokemonGlobal.bicycle
$PokemonGlobal.bicycle = true
$stats.cycle_count += 1
pbUpdateVehicle
bike_bgm = GameData::Metadata.get.bicycle_BGM
pbCueBGM(bike_bgm, 0.4) if bike_bgm
pbSEPlay("Bicycle")
pbPokeRadarCancel
end
def pbDismountBike
return if !$PokemonGlobal.bicycle
$PokemonGlobal.bicycle = false
pbUpdateVehicle
$game_map.autoplayAsCue
end

View File

@@ -0,0 +1,56 @@
#===============================================================================
# ** Game_CommonEvent
#-------------------------------------------------------------------------------
# This class handles common events. It includes execution of parallel process
# event. This class is used within the Game_Map class ($game_map).
#===============================================================================
class Game_CommonEvent
def initialize(common_event_id)
@common_event_id = common_event_id
@interpreter = nil
refresh
end
def name
return $data_common_events[@common_event_id].name
end
def trigger
return $data_common_events[@common_event_id].trigger
end
def switch_id
return $data_common_events[@common_event_id].switch_id
end
def list
return $data_common_events[@common_event_id].list
end
def switchIsOn?(id)
switchName = $data_system.switches[id]
return false if !switchName
if switchName[/^s\:/]
return eval($~.post_match)
else
return $game_switches[id]
end
end
def refresh
# Create an interpreter for parallel process if necessary
if self.trigger == 2 && switchIsOn?(self.switch_id)
@interpreter = Interpreter.new if @interpreter.nil?
else
@interpreter = nil
end
end
def update
return if !@interpreter
# Set up event if interpreter is not running
@interpreter.setup(self.list, 0) if !@interpreter.running?
# Update interpreter
@interpreter.update
end
end

View File

@@ -1,442 +0,0 @@
#===============================================================================
# ** Game_Player
#-------------------------------------------------------------------------------
# This class handles the player. Its functions include event starting
# determinants and map scrolling. Refer to "$game_player" for the one
# instance of this class.
#===============================================================================
class Game_Player < Game_Character
attr_accessor :bump_se
attr_accessor :charsetData
attr_accessor :encounter_count
SCREEN_CENTER_X = (Settings::SCREEN_WIDTH / 2 - Game_Map::TILE_WIDTH / 2) * Game_Map::X_SUBPIXELS
SCREEN_CENTER_Y = (Settings::SCREEN_HEIGHT / 2 - Game_Map::TILE_HEIGHT / 2) * Game_Map::Y_SUBPIXELS
def initialize(*arg)
super(*arg)
@lastdir=0
@lastdirframe=0
@bump_se=0
end
def map
@map = nil
return $game_map
end
def pbHasDependentEvents?
return $PokemonGlobal.dependentEvents.length>0
end
def bump_into_object
return if @bump_se && @bump_se>0
pbSEPlay("Player bump")
@bump_se = Graphics.frame_rate/4
end
def move_generic(dir, turn_enabled = true)
turn_generic(dir, true) if turn_enabled
if !$PokemonTemp.encounterTriggered
if can_move_in_direction?(dir)
x_offset = (dir == 4) ? -1 : (dir == 6) ? 1 : 0
y_offset = (dir == 8) ? -1 : (dir == 2) ? 1 : 0
return if pbLedge(x_offset, y_offset)
return if pbEndSurf(x_offset, y_offset)
turn_generic(dir, true)
if !$PokemonTemp.encounterTriggered
@x += x_offset
@y += y_offset
$PokemonTemp.dependentEvents.pbMoveDependentEvents
increase_steps
end
elsif !check_event_trigger_touch(dir)
bump_into_object
end
end
$PokemonTemp.encounterTriggered = false
end
def turn_generic(dir, keep_enc_indicator = false)
old_direction = @direction
super(dir)
if @direction != old_direction && !@move_route_forcing && !pbMapInterpreterRunning?
Events.onChangeDirection.trigger(self, self)
$PokemonTemp.encounterTriggered = false if !keep_enc_indicator
end
end
def pbTriggeredTrainerEvents(triggers,checkIfRunning=true)
result = []
# If event is running
return result if checkIfRunning && $game_system.map_interpreter.running?
# All event loops
for event in $game_map.events.values
next if !event.name[/trainer\((\d+)\)/i]
distance = $~[1].to_i
# If event coordinates and triggers are consistent
if pbEventCanReachPlayer?(event,self,distance) && triggers.include?(event.trigger)
# If starting determinant is front event (other than jumping)
result.push(event) if !event.jumping? && !event.over_trigger?
end
end
return result
end
def pbTriggeredCounterEvents(triggers,checkIfRunning=true)
result = []
# If event is running
return result if checkIfRunning && $game_system.map_interpreter.running?
# All event loops
for event in $game_map.events.values
next if !event.name[/counter\((\d+)\)/i]
distance = $~[1].to_i
# If event coordinates and triggers are consistent
if pbEventFacesPlayer?(event,self,distance) && triggers.include?(event.trigger)
# If starting determinant is front event (other than jumping)
result.push(event) if !event.jumping? && !event.over_trigger?
end
end
return result
end
def pbCheckEventTriggerAfterTurning; end
def pbCheckEventTriggerFromDistance(triggers)
ret = pbTriggeredTrainerEvents(triggers)
ret.concat(pbTriggeredCounterEvents(triggers))
return false if ret.length==0
for event in ret
event.start
end
return true
end
def pbTerrainTag(countBridge = false)
return $MapFactory.getTerrainTag(self.map.map_id, @x, @y, countBridge) if $MapFactory
return $game_map.terrain_tag(@x, @y, countBridge)
end
def pbFacingEvent(ignoreInterpreter=false)
return nil if $game_system.map_interpreter.running? && !ignoreInterpreter
# Check the tile in front of the player for events
new_x = @x + (@direction == 6 ? 1 : @direction == 4 ? -1 : 0)
new_y = @y + (@direction == 2 ? 1 : @direction == 8 ? -1 : 0)
return nil if !$game_map.valid?(new_x, new_y)
for event in $game_map.events.values
next if !event.at_coordinate?(new_x, new_y)
next if event.jumping? || event.over_trigger?
return event
end
# If the tile in front is a counter, check one tile beyond that for events
if $game_map.counter?(new_x, new_y)
new_x += (@direction == 6 ? 1 : @direction == 4 ? -1 : 0)
new_y += (@direction == 2 ? 1 : @direction == 8 ? -1 : 0)
for event in $game_map.events.values
next if !event.at_coordinate?(new_x, new_y)
next if event.jumping? || event.over_trigger?
return event
end
end
return nil
end
def pbFacingTerrainTag(dir = nil)
dir = self.direction if !dir
return $MapFactory.getFacingTerrainTag(dir, self) if $MapFactory
facing = pbFacingTile(dir, self)
return $game_map.terrain_tag(facing[1], facing[2])
end
#-----------------------------------------------------------------------------
# * Passable Determinants
# x : x-coordinate
# y : y-coordinate
# d : direction (0,2,4,6,8)
# * 0 = Determines if all directions are impassable (for jumping)
#-----------------------------------------------------------------------------
def passable?(x, y, d, strict = false)
# Get new coordinates
new_x = x + (d == 6 ? 1 : d == 4 ? -1 : 0)
new_y = y + (d == 2 ? 1 : d == 8 ? -1 : 0)
# If coordinates are outside of map
return false if !$game_map.validLax?(new_x, new_y)
if !$game_map.valid?(new_x, new_y)
return false if !$MapFactory
return $MapFactory.isPassableFromEdge?(new_x, new_y)
end
# If debug mode is ON and Ctrl key was pressed
return true if $DEBUG && Input.press?(Input::CTRL)
return super
end
#-----------------------------------------------------------------------------
# * Set Map Display Position to Center of Screen
#-----------------------------------------------------------------------------
def center(x, y)
self.map.display_x = x * Game_Map::REAL_RES_X - SCREEN_CENTER_X
self.map.display_y = y * Game_Map::REAL_RES_Y - SCREEN_CENTER_Y
end
#-----------------------------------------------------------------------------
# * Move to Designated Position
# x : x-coordinate
# y : y-coordinate
#-----------------------------------------------------------------------------
def moveto(x, y)
super
# Centering
center(x, y)
# Make encounter count
make_encounter_count
end
#-----------------------------------------------------------------------------
# * Make Encounter Count
#-----------------------------------------------------------------------------
def make_encounter_count
# Image of two dice rolling
if $game_map.map_id != 0
n = $game_map.encounter_step
@encounter_count = rand(n) + rand(n) + 1
end
end
#-----------------------------------------------------------------------------
# * Refresh
#-----------------------------------------------------------------------------
def refresh
@opacity = 255
@blend_type = 0
end
#-----------------------------------------------------------------------------
# * Trigger event(s) at the same coordinates as self with the appropriate
# trigger(s) that can be triggered
#-----------------------------------------------------------------------------
def check_event_trigger_here(triggers)
result = false
# If event is running
return result if $game_system.map_interpreter.running?
# All event loops
for event in $game_map.events.values
# If event coordinates and triggers are consistent
next if !event.at_coordinate?(@x, @y)
next if !triggers.include?(event.trigger)
# If starting determinant is same position event (other than jumping)
next if event.jumping? || !event.over_trigger?
event.start
result = true
end
return result
end
#-----------------------------------------------------------------------------
# * Front Event Starting Determinant
#-----------------------------------------------------------------------------
def check_event_trigger_there(triggers)
result = false
# If event is running
return result if $game_system.map_interpreter.running?
# Calculate front event coordinates
new_x = @x + (@direction == 6 ? 1 : @direction == 4 ? -1 : 0)
new_y = @y + (@direction == 2 ? 1 : @direction == 8 ? -1 : 0)
return false if !$game_map.valid?(new_x, new_y)
# All event loops
for event in $game_map.events.values
# If event coordinates and triggers are consistent
next if !event.at_coordinate?(new_x, new_y)
next if !triggers.include?(event.trigger)
# If starting determinant is front event (other than jumping)
next if event.jumping? || event.over_trigger?
event.start
result = true
end
# If fitting event is not found
if result == false
# If front tile is a counter
if $game_map.counter?(new_x, new_y)
# Calculate coordinates of 1 tile further away
new_x += (@direction == 6 ? 1 : @direction == 4 ? -1 : 0)
new_y += (@direction == 2 ? 1 : @direction == 8 ? -1 : 0)
return false if !$game_map.valid?(new_x, new_y)
# All event loops
for event in $game_map.events.values
# If event coordinates and triggers are consistent
next if !event.at_coordinate?(new_x, new_y)
next if !triggers.include?(event.trigger)
# If starting determinant is front event (other than jumping)
next if event.jumping? || event.over_trigger?
event.start
result = true
end
end
end
return result
end
#-----------------------------------------------------------------------------
# * Touch Event Starting Determinant
#-----------------------------------------------------------------------------
def check_event_trigger_touch(dir)
result = false
return result if $game_system.map_interpreter.running?
# All event loops
x_offset = (dir == 4) ? -1 : (dir == 6) ? 1 : 0
y_offset = (dir == 8) ? -1 : (dir == 2) ? 1 : 0
for event in $game_map.events.values
next if ![1, 2].include?(event.trigger) # Player touch, event touch
# If event coordinates and triggers are consistent
next if !event.at_coordinate?(@x + x_offset, @y + y_offset)
if event.name[/trainer\((\d+)\)/i]
distance = $~[1].to_i
next if !pbEventCanReachPlayer?(event,self,distance)
elsif event.name[/counter\((\d+)\)/i]
distance = $~[1].to_i
next if !pbEventFacesPlayer?(event,self,distance)
end
# If starting determinant is front event (other than jumping)
next if event.jumping? || event.over_trigger?
event.start
result = true
end
return result
end
#-----------------------------------------------------------------------------
# * Frame Update
#-----------------------------------------------------------------------------
def update
last_real_x = @real_x
last_real_y = @real_y
super
update_screen_position(last_real_x, last_real_y)
# Update dependent events
$PokemonTemp.dependentEvents.updateDependentEvents
# Count down the time between allowed bump sounds
@bump_se -= 1 if @bump_se && @bump_se>0
# Finish up dismounting from surfing
if $PokemonTemp.endSurf && !moving?
pbCancelVehicles
$PokemonTemp.surfJump = nil
$PokemonTemp.endSurf = false
end
update_event_triggering
end
def update_command_new
dir = Input.dir4
unless pbMapInterpreterRunning? || $game_temp.message_window_showing ||
$PokemonTemp.miniupdate || $game_temp.in_menu
# Move player in the direction the directional button is being pressed
if @moved_last_frame ||
(dir > 0 && dir == @lastdir && Graphics.frame_count - @lastdirframe > Graphics.frame_rate / 20)
case dir
when 2 then move_down
when 4 then move_left
when 6 then move_right
when 8 then move_up
end
elsif dir != @lastdir
case dir
when 2 then turn_down
when 4 then turn_left
when 6 then turn_right
when 8 then turn_up
end
end
end
# Record last direction input
@lastdirframe = Graphics.frame_count if dir != @lastdir
@lastdir = dir
end
# Center player on-screen
def update_screen_position(last_real_x, last_real_y)
return if self.map.scrolling? || !(@moved_last_frame || @moved_this_frame)
self.map.display_x = @real_x - SCREEN_CENTER_X
self.map.display_y = @real_y - SCREEN_CENTER_Y
end
def update_event_triggering
return if moving?
# Try triggering events upon walking into them/in front of them
if @moved_this_frame
$PokemonTemp.dependentEvents.pbTurnDependentEvents
result = pbCheckEventTriggerFromDistance([2])
# Event determinant is via touch of same position event
result |= check_event_trigger_here([1,2])
# No events triggered, try other event triggers upon finishing a step
pbOnStepTaken(result)
end
# Try to manually interact with events
if Input.trigger?(Input::USE) && !$PokemonTemp.miniupdate
# Same position and front event determinant
check_event_trigger_here([0])
check_event_trigger_there([0,2])
end
end
end
def pbGetPlayerCharset(meta,charset,trainer=nil,force=false)
trainer = $Trainer if !trainer
outfit = (trainer) ? trainer.outfit : 0
if $game_player && $game_player.charsetData && !force
return nil if $game_player.charsetData[0] == $Trainer.character_ID &&
$game_player.charsetData[1] == charset &&
$game_player.charsetData[2] == outfit
end
$game_player.charsetData = [$Trainer.character_ID,charset,outfit] if $game_player
ret = meta[charset]
ret = meta[1] if nil_or_empty?(ret)
if pbResolveBitmap("Graphics/Characters/"+ret+"_"+outfit.to_s)
ret = ret+"_"+outfit.to_s
end
return ret
end
def pbUpdateVehicle
meta = GameData::Metadata.get_player($Trainer.character_ID)
if meta
charset = 1 # Regular graphic
if $PokemonGlobal.diving; charset = 5 # Diving graphic
elsif $PokemonGlobal.surfing; charset = 3 # Surfing graphic
elsif $PokemonGlobal.bicycle; charset = 2 # Bicycle graphic
end
newCharName = pbGetPlayerCharset(meta,charset)
$game_player.character_name = newCharName if newCharName
end
end
def pbCancelVehicles(destination=nil)
$PokemonGlobal.surfing = false
$PokemonGlobal.diving = false
$PokemonGlobal.bicycle = false if !destination || !pbCanUseBike?(destination)
pbUpdateVehicle
end
def pbCanUseBike?(map_id)
map_metadata = GameData::MapMetadata.try_get(map_id)
return false if !map_metadata
return true if map_metadata.always_bicycle
val = map_metadata.can_bicycle || map_metadata.outdoor_map
return (val) ? true : false
end
def pbMountBike
return if $PokemonGlobal.bicycle
$PokemonGlobal.bicycle = true
pbUpdateVehicle
bike_bgm = GameData::Metadata.get.bicycle_BGM
pbCueBGM(bike_bgm, 0.5) if bike_bgm
pbPokeRadarCancel
end
def pbDismountBike
return if !$PokemonGlobal.bicycle
$PokemonGlobal.bicycle = false
pbUpdateVehicle
$game_map.autoplayAsCue
end

View File

@@ -0,0 +1,215 @@
#===============================================================================
# Instances of this are stored in @realEvents.
#===============================================================================
class Game_Follower < Game_Event
attr_writer :map
def initialize(event_data)
# Create RPG::Event to base self on
rpg_event = RPG::Event.new(event_data.x, event_data.y)
rpg_event.id = event_data.event_id
rpg_event.name = event_data.event_name
if event_data.common_event_id
# Must setup common event list here and now
common_event = Game_CommonEvent.new(event_data.common_event_id)
rpg_event.pages[0].list = common_event.list
end
# Create self
super(event_data.original_map_id, rpg_event, $map_factory.getMap(event_data.current_map_id))
# Modify self
self.character_name = event_data.character_name
self.character_hue = event_data.character_hue
case event_data.direction
when 2 then turn_down
when 4 then turn_left
when 6 then turn_right
when 8 then turn_up
end
end
def map_id
return @map.map_id
end
#-----------------------------------------------------------------------------
def move_through(direction)
old_through = @through
@through = true
case direction
when 2 then move_down
when 4 then move_left
when 6 then move_right
when 8 then move_up
end
@through = old_through
end
def move_fancy(direction)
delta_x = (direction == 6) ? 1 : (direction == 4) ? -1 : 0
delta_y = (direction == 2) ? 1 : (direction == 8) ? -1 : 0
new_x = self.x + delta_x
new_y = self.y + delta_y
# Move if new position is the player's, or the new position is passable,
# or self's current position is not passable
if ($game_player.x == new_x && $game_player.y == new_y) ||
location_passable?(new_x, new_y, 10 - direction) ||
!location_passable?(self.x, self.y, direction)
move_through(direction)
end
end
def jump_fancy(direction, leader)
delta_x = (direction == 6) ? 2 : (direction == 4) ? -2 : 0
delta_y = (direction == 2) ? 2 : (direction == 8) ? -2 : 0
half_delta_x = delta_x / 2
half_delta_y = delta_y / 2
if location_passable?(self.x + half_delta_x, self.y + half_delta_y, 10 - direction)
# Can walk over the middle tile normally; just take two steps
move_fancy(direction)
move_fancy(direction)
elsif location_passable?(self.x + delta_x, self.y + delta_y, 10 - direction)
# Can't walk over the middle tile, but can walk over the end tile; jump over
if location_passable?(self.x, self.y, direction)
if leader.jumping?
self.jump_speed = leader.jump_speed || 3
else
self.jump_speed = leader.move_speed || 3
# This is halved because self has to jump 2 tiles in the time it takes
# the leader to move one tile
@jump_time /= 2
end
jump(delta_x, delta_y)
else
# self's current tile isn't passable; just take two steps ignoring passability
move_through(direction)
move_through(direction)
end
end
end
def fancy_moveto(new_x, new_y, leader)
if self.x - new_x == 1 && self.y == new_y
move_fancy(4)
elsif self.x - new_x == -1 && self.y == new_y
move_fancy(6)
elsif self.x == new_x && self.y - new_y == 1
move_fancy(8)
elsif self.x == new_x && self.y - new_y == -1
move_fancy(2)
elsif self.x - new_x == 2 && self.y == new_y
jump_fancy(4, leader)
elsif self.x - new_x == -2 && self.y == new_y
jump_fancy(6, leader)
elsif self.x == new_x && self.y - new_y == 2
jump_fancy(8, leader)
elsif self.x == new_x && self.y - new_y == -2
jump_fancy(2, leader)
elsif self.x != new_x || self.y != new_y
moveto(new_x, new_y)
end
end
# Ceases all movement immediately. Used when the leader wants to move another
# tile but self hasn't quite finished its previous movement yet.
def end_movement
@x = x % self.map.width
@y = y % self.map.height
@real_x = @x * Game_Map::REAL_RES_X
@real_y = @y * Game_Map::REAL_RES_Y
@move_timer = nil
@jump_timer = nil
@jump_peak = 0
@jump_distance = 0
@jump_fraction = 0
@jumping_on_spot = false
end
#-----------------------------------------------------------------------------
def turn_towards_leader(leader)
pbTurnTowardEvent(self, leader)
end
def follow_leader(leader, instant = false, leaderIsTrueLeader = true)
return if @move_route_forcing
end_movement
maps_connected = $map_factory.areConnected?(leader.map.map_id, self.map.map_id)
target = nil
# Get the target tile that self wants to move to
if maps_connected
behind_direction = 10 - leader.direction
target = $map_factory.getFacingTile(behind_direction, leader)
if target && $map_factory.getTerrainTag(target[0], target[1], target[2]).ledge
# Get the tile above the ledge (where the leader jumped from)
target = $map_factory.getFacingTileFromPos(target[0], target[1], target[2], behind_direction)
end
target = [leader.map.map_id, leader.x, leader.y] if !target
else
# Map transfer to an unconnected map
target = [leader.map.map_id, leader.x, leader.y]
end
# Move self to the target
if self.map.map_id != target[0]
vector = $map_factory.getRelativePos(target[0], 0, 0, self.map.map_id, @x, @y)
@map = $map_factory.getMap(target[0])
# NOTE: Can't use moveto because vector is outside the boundaries of the
# map, and moveto doesn't allow setting invalid coordinates.
@x = vector[0]
@y = vector[1]
@real_x = @x * Game_Map::REAL_RES_X
@real_y = @y * Game_Map::REAL_RES_Y
end
if instant || !maps_connected
moveto(target[1], target[2])
else
fancy_moveto(target[1], target[2], leader)
end
end
#-----------------------------------------------------------------------------
private
def location_passable?(x, y, direction)
this_map = self.map
return false if !this_map || !this_map.valid?(x, y)
return true if @through
passed_tile_checks = false
bit = (1 << ((direction / 2) - 1)) & 0x0f
# Check all events for ones using tiles as graphics, and see if they're passable
this_map.events.each_value do |event|
next if event.tile_id < 0 || event.through || !event.at_coordinate?(x, y)
tile_data = GameData::TerrainTag.try_get(this_map.terrain_tags[event.tile_id])
next if tile_data.ignore_passability
next if tile_data.bridge && $PokemonGlobal.bridge == 0
return false if tile_data.ledge
passage = this_map.passages[event.tile_id] || 0
return false if passage & bit != 0
passed_tile_checks = true if (tile_data.bridge && $PokemonGlobal.bridge > 0) ||
(this_map.priorities[event.tile_id] || -1) == 0
break if passed_tile_checks
end
# Check if tiles at (x, y) allow passage for followe
if !passed_tile_checks
[2, 1, 0].each do |i|
tile_id = this_map.data[x, y, i] || 0
next if tile_id == 0
tile_data = GameData::TerrainTag.try_get(this_map.terrain_tags[tile_id])
next if tile_data.ignore_passability
next if tile_data.bridge && $PokemonGlobal.bridge == 0
return false if tile_data.ledge
passage = this_map.passages[tile_id] || 0
return false if passage & bit != 0
break if tile_data.bridge && $PokemonGlobal.bridge > 0
break if (this_map.priorities[tile_id] || -1) == 0
end
end
# Check all events on the map to see if any are in the way
this_map.events.each_value do |event|
next if !event.at_coordinate?(x, y)
return false if !event.through && event.character_name != ""
end
return true
end
end

View File

@@ -1,102 +0,0 @@
class Game_Player < Game_Character
@@bobFrameSpeed = 1.0/15
def fullPattern
case self.direction
when 2 then return self.pattern
when 4 then return self.pattern + 4
when 6 then return self.pattern + 8
when 8 then return self.pattern + 12
end
return 0
end
def setDefaultCharName(chname,pattern,lockpattern=false)
return if pattern<0 || pattern>=16
@defaultCharacterName = chname
@direction = [2,4,6,8][pattern/4]
@pattern = pattern%4
@lock_pattern = lockpattern
end
def pbCanRun?
return false if $game_temp.in_menu || $game_temp.in_battle ||
@move_route_forcing || $game_temp.message_window_showing ||
pbMapInterpreterRunning?
input = ($PokemonSystem.runstyle == 1) ^ Input.press?(Input::ACTION)
return input && $Trainer.has_running_shoes && !jumping? &&
!$PokemonGlobal.diving && !$PokemonGlobal.surfing &&
!$PokemonGlobal.bicycle && !$game_player.pbTerrainTag.must_walk
end
def pbIsRunning?
return moving? && !@move_route_forcing && pbCanRun?
end
def character_name
@defaultCharacterName = "" if !@defaultCharacterName
return @defaultCharacterName if @defaultCharacterName!=""
if !@move_route_forcing && $Trainer.character_ID>=0
meta = GameData::Metadata.get_player($Trainer.character_ID)
if meta && !$PokemonGlobal.bicycle && !$PokemonGlobal.diving && !$PokemonGlobal.surfing
charset = 1 # Display normal character sprite
if pbCanRun? && (moving? || @wasmoving) && Input.dir4!=0 && meta[4] && meta[4]!=""
charset = 4 # Display running character sprite
end
newCharName = pbGetPlayerCharset(meta,charset)
@character_name = newCharName if newCharName
@wasmoving = moving?
end
end
return @character_name
end
def update_command
if $game_player.pbTerrainTag.ice
self.move_speed = 4 # Sliding on ice
elsif !moving? && !@move_route_forcing && $PokemonGlobal
if $PokemonGlobal.bicycle
self.move_speed = 5 # Cycling
elsif pbCanRun? || $PokemonGlobal.surfing
self.move_speed = 4 # Running, surfing
else
self.move_speed = 3 # Walking, diving
end
end
super
end
def update_pattern
if $PokemonGlobal.surfing || $PokemonGlobal.diving
p = ((Graphics.frame_count%60)*@@bobFrameSpeed).floor
@pattern = p if !@lock_pattern
@pattern_surf = p
@bob_height = (p>=2) ? 2 : 0
else
@bob_height = 0
super
end
end
end
=begin
class Game_Character
alias update_old2 update
def update
if self.is_a?(Game_Event)
if @dependentEvents
for i in 0...@dependentEvents.length
if @dependentEvents[i][0]==$game_map.map_id &&
@dependentEvents[i][1]==self.id
self.move_speed_real = $game_player.move_speed_real
break
end
end
end
end
update_old2
end
end
=end

View File

@@ -1,81 +0,0 @@
#===============================================================================
# ** Game_CommonEvent
#-------------------------------------------------------------------------------
# This class handles common events. It includes execution of parallel process
# event. This class is used within the Game_Map class ($game_map).
#===============================================================================
class Game_CommonEvent
#-----------------------------------------------------------------------------
# * Object Initialization
# common_event_id : common event ID
#-----------------------------------------------------------------------------
def initialize(common_event_id)
@common_event_id = common_event_id
@interpreter = nil
refresh
end
#-----------------------------------------------------------------------------
# * Get Name
#-----------------------------------------------------------------------------
def name
return $data_common_events[@common_event_id].name
end
#-----------------------------------------------------------------------------
# * Get Trigger
#-----------------------------------------------------------------------------
def trigger
return $data_common_events[@common_event_id].trigger
end
#-----------------------------------------------------------------------------
# * Get Condition Switch ID
#-----------------------------------------------------------------------------
def switch_id
return $data_common_events[@common_event_id].switch_id
end
#-----------------------------------------------------------------------------
# * Get List of Event Commands
#-----------------------------------------------------------------------------
def list
return $data_common_events[@common_event_id].list
end
#-----------------------------------------------------------------------------
# * Checks if switch is on
#-----------------------------------------------------------------------------
def switchIsOn?(id)
switchName = $data_system.switches[id]
return false if !switchName
if switchName[/^s\:/]
return eval($~.post_match)
else
return $game_switches[id]
end
end
#-----------------------------------------------------------------------------
# * Refresh
#-----------------------------------------------------------------------------
def refresh
# Create an interpreter for parallel process if necessary
if self.trigger == 2 && switchIsOn?(self.switch_id)
if @interpreter == nil
@interpreter = Interpreter.new
end
else
@interpreter = nil
end
end
#-----------------------------------------------------------------------------
# * Frame Update
#-----------------------------------------------------------------------------
def update
# If parallel process is valid
if @interpreter != nil
# If not running
unless @interpreter.running?
# Set up event
@interpreter.setup(self.list, 0)
end
# Update interpreter
@interpreter.update
end
end
end

View File

@@ -0,0 +1,389 @@
#===============================================================================
# Data saved in $PokemonGlobal.followers.
#===============================================================================
class FollowerData
attr_accessor :original_map_id
attr_accessor :event_id
attr_accessor :event_name
attr_accessor :current_map_id
attr_accessor :x, :y
attr_accessor :direction
attr_accessor :character_name, :character_hue
attr_accessor :name
attr_accessor :common_event_id
attr_accessor :visible
attr_accessor :invisible_after_transfer
def initialize(original_map_id, event_id, event_name, current_map_id, x, y,
direction, character_name, character_hue)
@original_map_id = original_map_id
@event_id = event_id
@event_name = event_name
@current_map_id = current_map_id
@x = x
@y = y
@direction = direction
@character_name = character_name
@character_hue = character_hue
@name = nil
@common_event_id = nil
@visible = true
@invisible_after_transfer = false
end
def visible?
return @visible && !@invisible_after_transfer
end
def interact(event)
return if !event || event.list.size <= 1
return if !@common_event_id
# Start event
$game_map.refresh if $game_map.need_refresh
event.lock
pbMapInterpreter.setup(event.list, event.id, event.map.map_id)
end
end
#===============================================================================
# Permanently stores data of follower events (i.e. in save files).
#===============================================================================
class PokemonGlobalMetadata
attr_writer :followers
def followers
@followers = [] if !@followers
return @followers
end
end
#===============================================================================
# Stores Game_Follower instances just for the current play session.
#===============================================================================
class Game_Temp
attr_writer :followers
def followers
@followers = Game_FollowerFactory.new if !@followers
return @followers
end
end
#===============================================================================
#
#===============================================================================
class Game_FollowerFactory
attr_reader :last_update
def initialize
@events = []
$PokemonGlobal.followers.each do |follower|
@events.push(create_follower_object(follower))
end
@last_update = -1
end
#-----------------------------------------------------------------------------
def add_follower(event, name = nil, common_event_id = nil)
return if !event
followers = $PokemonGlobal.followers
if followers.any? { |data| data.original_map_id == $game_map.map_id && data.event_id == event.id }
return # Event is already dependent
end
eventData = FollowerData.new($game_map.map_id, event.id, event.name,
$game_map.map_id, event.x, event.y, event.direction,
event.character_name.clone, event.character_hue)
eventData.name = name
eventData.common_event_id = common_event_id
newEvent = create_follower_object(eventData)
followers.push(eventData)
@events.push(newEvent)
@last_update += 1
end
def remove_follower_by_event(event)
followers = $PokemonGlobal.followers
map_id = $game_map.map_id
followers.each_with_index do |follower, i|
next if follower.current_map_id != map_id
next if follower.original_map_id != event.map_id
next if follower.event_id != event.id
followers[i] = nil
@events[i] = nil
@last_update += 1
end
followers.compact!
@events.compact!
end
def remove_follower_by_name(name)
followers = $PokemonGlobal.followers
followers.each_with_index do |follower, i|
next if follower.name != name
followers[i] = nil
@events[i] = nil
@last_update += 1
end
followers.compact!
@events.compact!
end
def remove_all_followers
$PokemonGlobal.followers.clear
@events.clear
@last_update += 1
end
def get_follower_by_index(index = 0)
@events.each_with_index { |event, i| return event if i == index }
return nil
end
def get_follower_by_name(name)
each_follower { |event, follower| return event if follower&.name == name }
return nil
end
def each_follower
$PokemonGlobal.followers.each_with_index { |follower, i| yield @events[i], follower }
end
#-----------------------------------------------------------------------------
def turn_followers
leader = $game_player
$PokemonGlobal.followers.each_with_index do |follower, i|
event = @events[i]
event.turn_towards_leader(leader)
follower.direction = event.direction
leader = event
end
end
def move_followers
leader = $game_player
$PokemonGlobal.followers.each_with_index do |follower, i|
event = @events[i]
event.follow_leader(leader, false, (i == 0))
follower.x = event.x
follower.y = event.y
follower.current_map_id = event.map.map_id
follower.direction = event.direction
leader = event
end
end
def map_transfer_followers
$PokemonGlobal.followers.each_with_index do |follower, i|
event = @events[i]
event.map = $game_map
event.moveto($game_player.x, $game_player.y)
event.direction = $game_player.direction
event.opacity = 255
follower.x = event.x
follower.y = event.y
follower.current_map_id = event.map.map_id
follower.direction = event.direction
follower.invisible_after_transfer = true
end
end
def follow_into_door
# Setting an event's move route also makes it start along that move route,
# so we need to record all followers' current positions first before setting
# any move routes
follower_pos = []
follower_pos.push([$game_player.map.map_id, $game_player.x, $game_player.y])
$PokemonGlobal.followers.each_with_index do |follower, i|
event = @events[i]
follower_pos.push([event.map.map_id, event.x, event.y])
end
# Calculate and set move route from each follower to player
move_route = []
$PokemonGlobal.followers.each_with_index do |follower, i|
event = @events[i]
leader = follower_pos[i]
vector = $map_factory.getRelativePos(event.map.map_id, event.x, event.y,
leader[0], leader[1], leader[2])
if vector[0] != 0
move_route.prepend((vector[0] > 0) ? PBMoveRoute::RIGHT : PBMoveRoute::LEFT)
elsif vector[1] != 0
move_route.prepend((vector[1] > 0) ? PBMoveRoute::DOWN : PBMoveRoute::UP)
end
pbMoveRoute(event, move_route + [PBMoveRoute::OPACITY, 0])
end
end
# Used when coming out of a door.
def hide_followers
$PokemonGlobal.followers.each_with_index do |follower, i|
event = @events[i]
event.opacity = 0
end
end
# Used when coming out of a door. Makes all followers invisible until the
# player starts moving.
def put_followers_on_player
$PokemonGlobal.followers.each_with_index do |follower, i|
event = @events[i]
event.moveto($game_player.x, $game_player.y)
event.opacity = 255
follower.x = event.x
follower.y = event.y
follower.invisible_after_transfer = true
end
end
#-----------------------------------------------------------------------------
def update
return if $game_temp.in_menu
followers = $PokemonGlobal.followers
return if followers.length == 0
# Update all followers
leader = $game_player
player_moving = $game_player.moving? || $game_player.jumping?
followers.each_with_index do |follower, i|
event = @events[i]
next if !@events[i]
if follower.invisible_after_transfer && player_moving
follower.invisible_after_transfer = false
event.turn_towards_leader($game_player)
end
event.move_speed = leader.move_speed
event.transparent = !follower.visible?
if $PokemonGlobal.ice_sliding
event.straighten
event.walk_anime = false
else
event.walk_anime = true
end
if event.jumping? || event.moving? || !player_moving
event.update
elsif !event.starting
event.set_starting
event.update
event.clear_starting
end
follower.direction = event.direction
leader = event
end
# Check event triggers
if Input.trigger?(Input::USE) && !$game_temp.in_menu && !$game_temp.in_battle &&
!$game_player.move_route_forcing && !$game_temp.message_window_showing &&
!pbMapInterpreterRunning?
# Get position of tile facing the player
facing_tile = $map_factory.getFacingTile
# Assumes player is 1x1 tile in size
each_follower do |event, follower|
next if !facing_tile || event.map.map_id != facing_tile[0] ||
!event.at_coordinate?(facing_tile[1], facing_tile[2]) # Not on facing tile
next if event.jumping?
follower.interact(event)
end
end
end
#-----------------------------------------------------------------------------
private
def create_follower_object(event_data)
return Game_Follower.new(event_data)
end
end
#===============================================================================
#
#===============================================================================
class FollowerSprites
def initialize(viewport)
@viewport = viewport
@sprites = []
@last_update = nil
@disposed = false
end
def dispose
return if @disposed
@sprites.each { |sprite| sprite.dispose }
@sprites.clear
@disposed = true
end
def disposed?
return @disposed
end
def refresh
@sprites.each { |sprite| sprite.dispose }
@sprites.clear
$game_temp.followers.each_follower do |event, follower|
@sprites.push(Sprite_Character.new(@viewport, event))
end
end
def update
if $game_temp.followers.last_update != @last_update
refresh
@last_update = $game_temp.followers.last_update
end
@sprites.each { |sprite| sprite.update }
end
end
#===============================================================================
# Helper module for adding/removing/getting followers.
#===============================================================================
module Followers
module_function
# @param event_id [Integer] ID of the event on the current map to be added as a follower
# @param name [String] identifier name of the follower to be added
# @param common_event_id [Integer] ID of the Common Event triggered when interacting with this follower
def add(event_id, name, common_event_id)
$game_temp.followers.add_follower($game_map.events[event_id], name, common_event_id)
end
# @param event [Game_Event] map event to be added as a follower
def add_event(event)
$game_temp.followers.add_follower(event)
end
# @param name [String] identifier name of the follower to be removed
def remove(name)
$game_temp.followers.remove_follower_by_name(name)
end
# @param event [Game_Event] map event to be removed as a follower
def remove_event(event)
$game_temp.followers.remove_follower_by_event(event)
end
# Removes all followers.
def clear
$game_temp.followers.remove_all_followers
pbDeregisterPartner rescue nil
end
# @param name [String, nil] name of the follower to get, or nil for the first follower
# @return [Game_Follower, nil] follower object
def get(name = nil)
return $game_temp.followers.get_follower_by_name(name) if name
return $game_temp.followers.get_follower_by_index
end
def follow_into_door
$game_temp.followers.follow_into_door
end
def hide_followers
$game_temp.followers.hide_followers
end
def put_followers_on_player
$game_temp.followers.put_followers_on_player
end
end

View File

@@ -1,563 +0,0 @@
class PokemonTemp
attr_writer :dependentEvents
def dependentEvents
@dependentEvents = DependentEvents.new if !@dependentEvents
return @dependentEvents
end
end
def pbRemoveDependencies()
$PokemonTemp.dependentEvents.removeAllEvents()
pbDeregisterPartner() rescue nil
end
def pbAddDependency(event)
$PokemonTemp.dependentEvents.addEvent(event)
end
def pbRemoveDependency(event)
$PokemonTemp.dependentEvents.removeEvent(event)
end
def pbAddDependency2(eventID, eventName, commonEvent)
$PokemonTemp.dependentEvents.addEvent($game_map.events[eventID],eventName,commonEvent)
end
# Gets the Game_Character object associated with a dependent event.
def pbGetDependency(eventName)
return $PokemonTemp.dependentEvents.getEventByName(eventName)
end
def pbRemoveDependency2(eventName)
$PokemonTemp.dependentEvents.removeEventByName(eventName)
end
class PokemonGlobalMetadata
attr_writer :dependentEvents
def dependentEvents
@dependentEvents = [] if !@dependentEvents
return @dependentEvents
end
end
def pbTestPass(follower,x,y,_direction=nil)
return $MapFactory.isPassableStrict?(follower.map.map_id,x,y,follower)
end
# Same map only
def moveThrough(follower,direction)
oldThrough=follower.through
follower.through=true
case direction
when 2 then follower.move_down
when 4 then follower.move_left
when 6 then follower.move_right
when 8 then follower.move_up
end
follower.through=oldThrough
end
# Same map only
def moveFancy(follower,direction)
deltaX=(direction == 6 ? 1 : (direction == 4 ? -1 : 0))
deltaY=(direction == 2 ? 1 : (direction == 8 ? -1 : 0))
newX = follower.x + deltaX
newY = follower.y + deltaY
# Move if new position is the player's, or the new position is passable,
# or the current position is not passable
if ($game_player.x==newX && $game_player.y==newY) ||
pbTestPass(follower,newX,newY,0) ||
!pbTestPass(follower,follower.x,follower.y,0)
oldThrough=follower.through
follower.through=true
case direction
when 2 then follower.move_down
when 4 then follower.move_left
when 6 then follower.move_right
when 8 then follower.move_up
end
follower.through=oldThrough
end
end
# Same map only
def jumpFancy(follower,direction,leader)
deltaX=(direction == 6 ? 2 : (direction == 4 ? -2 : 0))
deltaY=(direction == 2 ? 2 : (direction == 8 ? -2 : 0))
halfDeltaX=(direction == 6 ? 1 : (direction == 4 ? -1 : 0))
halfDeltaY=(direction == 2 ? 1 : (direction == 8 ? -1 : 0))
middle=pbTestPass(follower,follower.x+halfDeltaX,follower.y+halfDeltaY,0)
ending=pbTestPass(follower,follower.x+deltaX, follower.y+deltaY, 0)
if middle
moveFancy(follower,direction)
moveFancy(follower,direction)
elsif ending
if pbTestPass(follower,follower.x,follower.y,0)
if leader.jumping?
follower.jump_speed_real = leader.jump_speed_real * Graphics.frame_rate / 40.0
else
follower.jump_speed_real = leader.move_speed_real * Graphics.frame_rate / 20.0
end
follower.jump(deltaX,deltaY)
else
moveThrough(follower,direction)
moveThrough(follower,direction)
end
end
end
def pbFancyMoveTo(follower,newX,newY,leader)
if follower.x-newX==-1 && follower.y==newY
moveFancy(follower,6)
elsif follower.x-newX==1 && follower.y==newY
moveFancy(follower,4)
elsif follower.y-newY==-1 && follower.x==newX
moveFancy(follower,2)
elsif follower.y-newY==1 && follower.x==newX
moveFancy(follower,8)
elsif follower.x-newX==-2 && follower.y==newY
jumpFancy(follower,6,leader)
elsif follower.x-newX==2 && follower.y==newY
jumpFancy(follower,4,leader)
elsif follower.y-newY==-2 && follower.x==newX
jumpFancy(follower,2,leader)
elsif follower.y-newY==2 && follower.x==newX
jumpFancy(follower,8,leader)
elsif follower.x!=newX || follower.y!=newY
follower.moveto(newX,newY)
end
end
class DependentEvents
attr_reader :lastUpdate
def createEvent(eventData)
rpgEvent = RPG::Event.new(eventData[3],eventData[4])
rpgEvent.id = eventData[1]
if eventData[9]
# Must setup common event list here and now
commonEvent = Game_CommonEvent.new(eventData[9])
rpgEvent.pages[0].list = commonEvent.list
end
newEvent = Game_Event.new(eventData[0],rpgEvent,$MapFactory.getMap(eventData[2]))
newEvent.character_name = eventData[6]
newEvent.character_hue = eventData[7]
case eventData[5] # direction
when 2 then newEvent.turn_down
when 4 then newEvent.turn_left
when 6 then newEvent.turn_right
when 8 then newEvent.turn_up
end
return newEvent
end
def initialize
# Original map, Event ID, Current map, X, Y, Direction
events=$PokemonGlobal.dependentEvents
@realEvents=[]
@lastUpdate=-1
for event in events
@realEvents.push(createEvent(event))
end
end
def pbEnsureEvent(event, newMapID)
events=$PokemonGlobal.dependentEvents
for i in 0...events.length
# Check original map ID and original event ID
if events[i][0]==event.map_id && events[i][1]==event.id
# Change current map ID
events[i][2]=newMapID
newEvent=createEvent(events[i])
# Replace event
@realEvents[i]=newEvent
@lastUpdate+=1
return i
end
end
return -1
end
def pbFollowEventAcrossMaps(leader,follower,instant=false,leaderIsTrueLeader=true)
d=leader.direction
areConnected=$MapFactory.areConnected?(leader.map.map_id,follower.map.map_id)
# Get the rear facing tile of leader
facingDirection=10-d
if !leaderIsTrueLeader && areConnected
relativePos=$MapFactory.getThisAndOtherEventRelativePos(leader,follower)
# Assumes leader and follower are both 1x1 tile in size
if (relativePos[1]==0 && relativePos[0]==2) # 2 spaces to the right of leader
facingDirection=6
elsif (relativePos[1]==0 && relativePos[0]==-2) # 2 spaces to the left of leader
facingDirection=4
elsif relativePos[1]==-2 && relativePos[0]==0 # 2 spaces above leader
facingDirection=8
elsif relativePos[1]==2 && relativePos[0]==0 # 2 spaces below leader
facingDirection=2
end
end
facings=[facingDirection] # Get facing from behind
# facings.push([0,0,4,0,8,0,2,0,6][d]) # Get right facing
# facings.push([0,0,6,0,2,0,8,0,4][d]) # Get left facing
if !leaderIsTrueLeader
facings.push(d) # Get forward facing
end
mapTile=nil
if areConnected
bestRelativePos=-1
oldthrough=follower.through
follower.through=false
for i in 0...facings.length
facing=facings[i]
tile=$MapFactory.getFacingTile(facing,leader)
# Assumes leader is 1x1 tile in size
passable=tile && $MapFactory.isPassableStrict?(tile[0],tile[1],tile[2],follower)
if i==0 && !passable && tile &&
$MapFactory.getTerrainTag(tile[0],tile[1],tile[2]).ledge
# If the tile isn't passable and the tile is a ledge,
# get tile from further behind
tile=$MapFactory.getFacingTileFromPos(tile[0],tile[1],tile[2],facing)
passable=tile && $MapFactory.isPassableStrict?(tile[0],tile[1],tile[2],follower)
end
if passable
relativePos=$MapFactory.getThisAndOtherPosRelativePos(
follower,tile[0],tile[1],tile[2])
# Assumes follower is 1x1 tile in size
distance=Math.sqrt(relativePos[0]*relativePos[0]+relativePos[1]*relativePos[1])
if bestRelativePos==-1 || bestRelativePos>distance
bestRelativePos=distance
mapTile=tile
end
if i==0 && distance<=1 # Prefer behind if tile can move up to 1 space
break
end
end
end
follower.through=oldthrough
else
tile=$MapFactory.getFacingTile(facings[0],leader)
# Assumes leader is 1x1 tile in size
passable=tile && $MapFactory.isPassableStrict?(tile[0],tile[1],tile[2],follower)
mapTile=passable ? mapTile : nil
end
if mapTile && follower.map.map_id==mapTile[0]
# Follower is on same map
newX=mapTile[1]
newY=mapTile[2]
deltaX=(d == 6 ? -1 : d == 4 ? 1 : 0)
deltaY=(d == 2 ? -1 : d == 8 ? 1 : 0)
posX = newX + deltaX
posY = newY + deltaY
follower.move_speed=leader.move_speed # sync movespeed
if (follower.x-newX==-1 && follower.y==newY) ||
(follower.x-newX==1 && follower.y==newY) ||
(follower.y-newY==-1 && follower.x==newX) ||
(follower.y-newY==1 && follower.x==newX)
if instant
follower.moveto(newX,newY)
else
pbFancyMoveTo(follower,newX,newY,leader)
end
elsif (follower.x-newX==-2 && follower.y==newY) ||
(follower.x-newX==2 && follower.y==newY) ||
(follower.y-newY==-2 && follower.x==newX) ||
(follower.y-newY==2 && follower.x==newX)
if instant
follower.moveto(newX,newY)
else
pbFancyMoveTo(follower,newX,newY,leader)
end
elsif follower.x!=posX || follower.y!=posY
if instant
follower.moveto(newX,newY)
else
pbFancyMoveTo(follower,posX,posY,leader)
pbFancyMoveTo(follower,newX,newY,leader)
end
end
else
if !mapTile
# Make current position into leader's position
mapTile=[leader.map.map_id,leader.x,leader.y]
end
if follower.map.map_id==mapTile[0]
# Follower is on same map as leader
follower.moveto(leader.x,leader.y)
else
# Follower will move to different map
events=$PokemonGlobal.dependentEvents
eventIndex=pbEnsureEvent(follower,mapTile[0])
if eventIndex>=0
newFollower=@realEvents[eventIndex]
newEventData=events[eventIndex]
newFollower.moveto(mapTile[1],mapTile[2])
newEventData[3]=mapTile[1]
newEventData[4]=mapTile[2]
end
end
end
end
def debugEcho
self.eachEvent { |e,d|
echoln d
echoln [e.map_id,e.map.map_id,e.id]
}
end
def pbMapChangeMoveDependentEvents
events=$PokemonGlobal.dependentEvents
updateDependentEvents
leader=$game_player
for i in 0...events.length
event=@realEvents[i]
pbFollowEventAcrossMaps(leader,event,true,i==0)
# Update X and Y for this event
events[i][3]=event.x
events[i][4]=event.y
events[i][5]=event.direction
# Set leader to this event
leader=event
end
end
def pbMoveDependentEvents
events=$PokemonGlobal.dependentEvents
updateDependentEvents
leader=$game_player
for i in 0...events.length
event=@realEvents[i]
pbFollowEventAcrossMaps(leader,event,false,i==0)
# Update X and Y for this event
events[i][3]=event.x
events[i][4]=event.y
events[i][5]=event.direction
# Set leader to this event
leader=event
end
end
def pbTurnDependentEvents
events=$PokemonGlobal.dependentEvents
updateDependentEvents
leader=$game_player
for i in 0...events.length
event=@realEvents[i]
pbTurnTowardEvent(event,leader)
# Update direction for this event
events[i][5]=event.direction
# Set leader to this event
leader=event
end
end
def eachEvent
events=$PokemonGlobal.dependentEvents
for i in 0...events.length
yield @realEvents[i],events[i]
end
end
def updateDependentEvents
events=$PokemonGlobal.dependentEvents
return if events.length==0
for i in 0...events.length
event=@realEvents[i]
next if !@realEvents[i]
event.transparent=$game_player.transparent
if event.jumping? || event.moving? ||
!($game_player.jumping? || $game_player.moving?)
event.update
elsif !event.starting
event.set_starting
event.update
event.clear_starting
end
events[i][3]=event.x
events[i][4]=event.y
events[i][5]=event.direction
end
# Check event triggers
if Input.trigger?(Input::USE) && !$game_temp.in_menu && !$game_temp.in_battle &&
!$game_player.move_route_forcing && !$game_temp.message_window_showing &&
!pbMapInterpreterRunning?
# Get position of tile facing the player
facingTile=$MapFactory.getFacingTile()
# Assumes player is 1x1 tile in size
self.eachEvent { |e,d|
next if !d[9]
if e.at_coordinate?($game_player.x, $game_player.y)
# On same position
if !e.jumping? && (!e.respond_to?("over_trigger") || e.over_trigger?)
if e.list.size>1
# Start event
$game_map.refresh if $game_map.need_refresh
e.lock
pbMapInterpreter.setup(e.list,e.id,e.map.map_id)
end
end
elsif facingTile && e.map.map_id==facingTile[0] &&
e.at_coordinate?(facingTile[1], facingTile[2])
# On facing tile
if !e.jumping? && (!e.respond_to?("over_trigger") || !e.over_trigger?)
if e.list.size>1
# Start event
$game_map.refresh if $game_map.need_refresh
e.lock
pbMapInterpreter.setup(e.list,e.id,e.map.map_id)
end
end
end
}
end
end
def removeEvent(event)
events=$PokemonGlobal.dependentEvents
mapid=$game_map.map_id
for i in 0...events.length
if events[i][2]==mapid && # Refer to current map
events[i][0]==event.map_id && # Event's map ID is original ID
events[i][1]==event.id
events[i]=nil
@realEvents[i]=nil
@lastUpdate+=1
end
end
events.compact!
@realEvents.compact!
end
def getEventByName(name)
events=$PokemonGlobal.dependentEvents
for i in 0...events.length
if events[i] && events[i][8]==name # Arbitrary name given to dependent event
return @realEvents[i]
end
end
return nil
end
def removeAllEvents
events=$PokemonGlobal.dependentEvents
events.clear
@realEvents.clear
@lastUpdate+=1
end
def removeEventByName(name)
events=$PokemonGlobal.dependentEvents
for i in 0...events.length
if events[i] && events[i][8]==name # Arbitrary name given to dependent event
events[i]=nil
@realEvents[i]=nil
@lastUpdate+=1
end
end
events.compact!
@realEvents.compact!
end
def addEvent(event,eventName=nil,commonEvent=nil)
return if !event
events=$PokemonGlobal.dependentEvents
for i in 0...events.length
if events[i] && events[i][0]==$game_map.map_id && events[i][1]==event.id
# Already exists
return
end
end
# Original map ID, original event ID, current map ID,
# event X, event Y, event direction,
# event's filename,
# event's hue, event's name, common event ID
eventData=[
$game_map.map_id,event.id,$game_map.map_id,
event.x,event.y,event.direction,
event.character_name.clone,
event.character_hue,eventName,commonEvent
]
newEvent=createEvent(eventData)
events.push(eventData)
@realEvents.push(newEvent)
@lastUpdate+=1
event.erase
end
end
class DependentEventSprites
def initialize(viewport,map)
@disposed=false
@sprites=[]
@map=map
@viewport=viewport
refresh
@lastUpdate=nil
end
def refresh
for sprite in @sprites
sprite.dispose
end
@sprites.clear
$PokemonTemp.dependentEvents.eachEvent { |event,data|
if data[0]==@map.map_id # Check original map
@map.events[data[1]].erase
end
if data[2]==@map.map_id # Check current map
@sprites.push(Sprite_Character.new(@viewport,event))
end
}
end
def update
if $PokemonTemp.dependentEvents.lastUpdate!=@lastUpdate
refresh
@lastUpdate=$PokemonTemp.dependentEvents.lastUpdate
end
for sprite in @sprites
sprite.update
end
end
def dispose
return if @disposed
for sprite in @sprites
sprite.dispose
end
@sprites.clear
@disposed=true
end
def disposed?
@disposed
end
end
Events.onSpritesetCreate += proc { |_sender,e|
spriteset = e[0] # Spriteset being created
viewport = e[1] # Viewport used for tilemap and characters
map = spriteset.map # Map associated with the spriteset (not necessarily the current map)
spriteset.addUserSprite(DependentEventSprites.new(viewport,map))
}
Events.onMapSceneChange += proc { |_sender,e|
mapChanged = e[1]
if mapChanged
$PokemonTemp.dependentEvents.pbMapChangeMoveDependentEvents
end
}

View File

@@ -0,0 +1,214 @@
#===============================================================================
# Stored in $stats
#===============================================================================
class GameStats
# Travel
attr_accessor :distance_walked, :distance_cycled, :distance_surfed # surfed includes diving
attr_accessor :distance_slid_on_ice # Also counted in distance_walked
attr_accessor :bump_count # Times the player walked into something
attr_accessor :cycle_count, :surf_count, :dive_count
# Field actions
attr_accessor :fly_count, :cut_count, :flash_count
attr_accessor :rock_smash_count, :rock_smash_battles
attr_accessor :headbutt_count, :headbutt_battles
attr_accessor :strength_push_count # Number of shoves, not the times Strength was used
attr_accessor :waterfall_count, :waterfalls_descended
# Items
attr_accessor :repel_count
attr_accessor :itemfinder_count
attr_accessor :fishing_count, :fishing_battles
attr_accessor :poke_radar_count, :poke_radar_longest_chain
attr_accessor :berry_plants_picked, :max_yield_berry_plants
attr_accessor :berries_planted
# NPCs
attr_accessor :poke_center_count
attr_accessor :revived_fossil_count
attr_accessor :lottery_prize_count # Times won any prize at all
# Pokémon
attr_accessor :eggs_hatched
attr_accessor :evolution_count, :evolutions_cancelled
attr_accessor :trade_count
attr_accessor :pokemon_release_count
attr_accessor :moves_taught_by_item, :moves_taught_by_tutor, :moves_taught_by_reminder
attr_accessor :day_care_deposits, :day_care_levels_gained
attr_accessor :pokerus_infections
attr_accessor :shadow_pokemon_purified
# Battles
attr_accessor :wild_battles_won, :wild_battles_lost, :wild_battles_fled # Fled counts both player and wild Pokémon fleeing
attr_accessor :trainer_battles_won, :trainer_battles_lost
attr_accessor :total_exp_gained
attr_accessor :battle_money_gained, :battle_money_lost
attr_accessor :blacked_out_count
attr_accessor :mega_evolution_count, :primal_reversion_count
attr_accessor :failed_poke_ball_count
# Currency
attr_accessor :money_spent_at_marts
attr_accessor :money_earned_at_marts
attr_accessor :mart_items_bought, :premier_balls_earned
attr_accessor :drinks_bought, :drinks_won # From vending machines
attr_accessor :coins_won, :coins_lost # Not bought, not spent
attr_accessor :battle_points_won, :battle_points_spent
attr_accessor :soot_collected
# Special stats
attr_accessor :gym_leader_attempts # An array of integers
attr_accessor :times_to_get_badges # An array of times in seconds
attr_accessor :elite_four_attempts
attr_accessor :hall_of_fame_entry_count # See also Game Variable 13
attr_accessor :time_to_enter_hall_of_fame # In seconds
attr_accessor :safari_pokemon_caught, :most_captures_per_safari_game
attr_accessor :bug_contest_count, :bug_contest_wins
# Play
attr_writer :play_time # In seconds; the reader also updates the value
attr_accessor :play_sessions
attr_accessor :time_last_saved # In seconds
attr_reader :real_time_saved
attr_accessor :save_filename_number # -1 if haven't saved yet
def initialize
# Travel
@distance_walked = 0
@distance_cycled = 0
@distance_surfed = 0
@distance_slid_on_ice = 0
@bump_count = 0
@cycle_count = 0
@surf_count = 0
@dive_count = 0
# Field actions
@fly_count = 0
@cut_count = 0
@flash_count = 0
@rock_smash_count = 0
@rock_smash_battles = 0
@headbutt_count = 0
@headbutt_battles = 0
@strength_push_count = 0
@waterfall_count = 0
@waterfalls_descended = 0
# Items
@repel_count = 0
@itemfinder_count = 0
@fishing_count = 0
@fishing_battles = 0
@poke_radar_count = 0
@poke_radar_longest_chain = 0
@berry_plants_picked = 0
@max_yield_berry_plants = 0
@berries_planted = 0
# NPCs
@poke_center_count = 0 # Incremented in Poké Center nurse events
@revived_fossil_count = 0 # Incremented in fossil reviver events
@lottery_prize_count = 0 # Incremented in lottery NPC events
# Pokémon
@eggs_hatched = 0
@evolution_count = 0
@evolutions_cancelled = 0
@trade_count = 0
@pokemon_release_count = 0
@moves_taught_by_item = 0
@moves_taught_by_tutor = 0
@moves_taught_by_reminder = 0
@day_care_deposits = 0
@day_care_levels_gained = 0
@pokerus_infections = 0
@shadow_pokemon_purified = 0
# Battles
@wild_battles_won = 0
@wild_battles_lost = 0
@wild_battles_fled = 0
@trainer_battles_won = 0
@trainer_battles_lost = 0
@total_exp_gained = 0
@battle_money_gained = 0
@battle_money_lost = 0
@blacked_out_count = 0
@mega_evolution_count = 0
@primal_reversion_count = 0
@failed_poke_ball_count = 0
# Currency
@money_spent_at_marts = 0
@money_earned_at_marts = 0
@mart_items_bought = 0
@premier_balls_earned = 0
@drinks_bought = 0 # Incremented in vending machine events
@drinks_won = 0 # Incremented in vending machine events
@coins_won = 0
@coins_lost = 0
@battle_points_won = 0
@battle_points_spent = 0
@soot_collected = 0
# Special stats
@gym_leader_attempts = [0] * 50 # Incremented in Gym Leader events (50 is arbitrary but suitably large)
@times_to_get_badges = [] # Set with set_time_to_badge(number) in Gym Leader events
@elite_four_attempts = 0 # Incremented in door event leading to the first E4 member
@hall_of_fame_entry_count = 0 # Incremented in Hall of Fame event
@time_to_enter_hall_of_fame = 0 # Set with set_time_to_hall_of_fame in Hall of Fame event
@safari_pokemon_caught = 0
@most_captures_per_safari_game = 0
@bug_contest_count = 0
@bug_contest_wins = 0
# Play
@play_time = 0
@play_sessions = 0
@time_last_saved = 0
@real_time_saved = 0
@save_filename_number = -1
end
def distance_moved
return @distance_walked + @distance_cycled + @distance_surfed
end
def caught_pokemon_count
return 0 if !$player
ret = 0
GameData::Species.each_species { |sp| ret += $player.pokedex.caught_count(sp) }
return ret
end
def save_count
return $game_system&.save_count || 0
end
def set_time_to_badge(number)
@times_to_get_badges[number] = play_time
end
def set_time_to_hall_of_fame
@time_to_enter_hall_of_fame = play_time if @time_to_enter_hall_of_fame == 0
end
def play_time
if $game_temp&.last_uptime_refreshed_play_time
now = System.uptime
@play_time += now - $game_temp.last_uptime_refreshed_play_time
$game_temp.last_uptime_refreshed_play_time = now
end
return @play_time
end
# For looking at a save file's play time.
def real_play_time
return @play_time
end
def play_time_per_session
return play_time / @play_sessions
end
def set_time_last_saved
@time_last_saved = play_time
@real_time_saved = Time.now.to_i
end
def time_since_last_save
return play_time - @time_last_saved
end
end
#===============================================================================
#
#===============================================================================
class Game_Temp
attr_accessor :last_uptime_refreshed_play_time
end

View File

@@ -1,3 +1,6 @@
#===============================================================================
#
#===============================================================================
class Sprite_Picture
def initialize(viewport, picture)
@viewport = viewport
@@ -7,11 +10,11 @@ class Sprite_Picture
end
def dispose
@sprite.dispose if @sprite
@sprite&.dispose
end
def update
@sprite.update if @sprite
@sprite&.update
# If picture file name is different from current one
if @picture_name != @picture.name
# Remember file name to instance variables
@@ -19,16 +22,16 @@ class Sprite_Picture
# If file name is not empty
if @picture_name != ""
# Get picture graphic
@sprite=IconSprite.new(0,0,@viewport) if !@sprite
@sprite.setBitmap("Graphics/Pictures/"+@picture_name)
@sprite = IconSprite.new(0, 0, @viewport) if !@sprite
@sprite.setBitmap("Graphics/Pictures/" + @picture_name)
end
end
# If file name is empty
if @picture_name == ""
# Set sprite to invisible
if @sprite
@sprite.dispose if @sprite
@sprite=nil
@sprite&.dispose
@sprite = nil
end
return
end

View File

@@ -1,15 +1,18 @@
#===============================================================================
#
#===============================================================================
class Sprite_Timer
def initialize(viewport=nil)
@viewport=viewport
@timer=nil
@total_sec=nil
@disposed=false
def initialize(viewport = nil)
@viewport = viewport
@timer = nil
@total_sec = nil
@disposed = false
end
def dispose
@timer.dispose if @timer
@timer=nil
@disposed=true
@timer&.dispose
@timer = nil
@disposed = true
end
def disposed?
@@ -18,17 +21,17 @@ class Sprite_Timer
def update
return if disposed?
if $game_system.timer_working
if $game_system.timer_start
@timer.visible = true if @timer
if !@timer
@timer=Window_AdvancedTextPokemon.newWithSize("",Graphics.width-120,0,120,64)
@timer.width=@timer.borderX+96
@timer.x=Graphics.width-@timer.width
@timer.viewport=@viewport
@timer.z=99998
@timer = Window_AdvancedTextPokemon.newWithSize("", Graphics.width - 120, 0, 120, 64)
@timer.width = @timer.borderX + 96
@timer.x = Graphics.width - @timer.width
@timer.viewport = @viewport
@timer.z = 99998
end
curtime=$game_system.timer / Graphics.frame_rate
curtime=0 if curtime<0
curtime = $game_system.timer
curtime = 0 if curtime < 0
if curtime != @total_sec
# Calculate total number of seconds
@total_sec = curtime
@@ -38,8 +41,8 @@ class Sprite_Timer
@timer.text = _ISPRINTF("<ac>{1:02d}:{2:02d}", min, sec)
end
@timer.update
else
@timer.visible=false if @timer
elsif @timer
@timer.visible = false
end
end
end

View File

@@ -1,3 +1,6 @@
#===============================================================================
#
#===============================================================================
class BushBitmap
def initialize(bitmap, isTile, depth)
@bitmaps = []
@@ -8,7 +11,7 @@ class BushBitmap
end
def dispose
@bitmaps.each { |b| b.dispose if b }
@bitmaps.each { |b| b&.dispose }
end
def bitmap
@@ -28,7 +31,7 @@ class BushBitmap
ret = Bitmap.new(bitmap.width, bitmap.height)
charheight = ret.height / 4
cy = charheight - depth - 2
for i in 0...4
4.times do |i|
y = i * charheight
if cy >= 0
ret.blt(0, y, bitmap, Rect.new(0, y, ret.width, cy))
@@ -53,8 +56,9 @@ class BushBitmap
end
end
#===============================================================================
#
#===============================================================================
class Sprite_Character < RPG::Sprite
attr_accessor :character
@@ -64,9 +68,11 @@ class Sprite_Character < RPG::Sprite
@oldbushdepth = 0
@spriteoffset = false
if !character || character == $game_player || (character.name[/reflection/i] rescue false)
@reflection = Sprite_Reflection.new(self, character, viewport)
@reflection = Sprite_Reflection.new(self, viewport)
end
@surfbase = Sprite_SurfBase.new(self, character, viewport) if character == $game_player
@surfbase = Sprite_SurfBase.new(self, viewport) if character == $game_player
self.zoom_x = TilemapRenderer::ZOOM_X
self.zoom_y = TilemapRenderer::ZOOM_Y
update
end
@@ -80,57 +86,66 @@ class Sprite_Character < RPG::Sprite
end
def dispose
@bushbitmap.dispose if @bushbitmap
@bushbitmap&.dispose
@bushbitmap = nil
@charbitmap.dispose if @charbitmap
@charbitmap&.dispose
@charbitmap = nil
@reflection.dispose if @reflection
@reflection&.dispose
@reflection = nil
@surfbase.dispose if @surfbase
@surfbase&.dispose
@surfbase = nil
@character = nil
super
end
def refresh_graphic
return if @tile_id == @character.tile_id &&
@character_name == @character.character_name &&
@character_hue == @character.character_hue &&
@oldbushdepth == @character.bush_depth
@tile_id = @character.tile_id
@character_name = @character.character_name
@character_hue = @character.character_hue
@oldbushdepth = @character.bush_depth
@charbitmap&.dispose
@charbitmap = nil
@bushbitmap&.dispose
@bushbitmap = nil
if @tile_id >= 384
@charbitmap = pbGetTileBitmap(@character.map.tileset_name, @tile_id,
@character_hue, @character.width, @character.height)
@charbitmapAnimated = false
@spriteoffset = false
@cw = Game_Map::TILE_WIDTH * @character.width
@ch = Game_Map::TILE_HEIGHT * @character.height
self.src_rect.set(0, 0, @cw, @ch)
self.ox = @cw / 2
self.oy = @ch
elsif @character_name != ""
@charbitmap = AnimatedBitmap.new(
"Graphics/Characters/" + @character_name, @character_hue
)
RPG::Cache.retain("Graphics/Characters/", @character_name, @character_hue) if @character == $game_player
@charbitmapAnimated = true
@spriteoffset = @character_name[/offset/i]
@cw = @charbitmap.width / 4
@ch = @charbitmap.height / 4
self.ox = @cw / 2
else
self.bitmap = nil
@cw = 0
@ch = 0
@reflection&.update
end
@character.sprite_size = [@cw, @ch]
end
def update
return if @character.is_a?(Game_Event) && !@character.should_update?
super
if @tile_id != @character.tile_id ||
@character_name != @character.character_name ||
@character_hue != @character.character_hue ||
@oldbushdepth != @character.bush_depth
@tile_id = @character.tile_id
@character_name = @character.character_name
@character_hue = @character.character_hue
@oldbushdepth = @character.bush_depth
if @tile_id >= 384
@charbitmap.dispose if @charbitmap
@charbitmap = pbGetTileBitmap(@character.map.tileset_name, @tile_id,
@character_hue, @character.width, @character.height)
@charbitmapAnimated = false
@bushbitmap.dispose if @bushbitmap
@bushbitmap = nil
@spriteoffset = false
@cw = Game_Map::TILE_WIDTH * @character.width
@ch = Game_Map::TILE_HEIGHT * @character.height
self.src_rect.set(0, 0, @cw, @ch)
self.ox = @cw / 2
self.oy = @ch
@character.sprite_size = [@cw, @ch]
else
@charbitmap.dispose if @charbitmap
@charbitmap = AnimatedBitmap.new(
'Graphics/Characters/' + @character_name, @character_hue)
RPG::Cache.retain('Graphics/Characters/', @character_name, @character_hue) if @character == $game_player
@charbitmapAnimated = true
@bushbitmap.dispose if @bushbitmap
@bushbitmap = nil
@spriteoffset = @character_name[/offset/i]
@cw = @charbitmap.width / 4
@ch = @charbitmap.height / 4
self.ox = @cw / 2
@character.sprite_size = [@cw, @ch]
end
end
refresh_graphic
return if !@charbitmap
@charbitmap.update if @charbitmapAnimated
bushdepth = @character.bush_depth
if bushdepth == 0
@@ -154,20 +169,21 @@ class Sprite_Character < RPG::Sprite
pbDayNightTint(self)
end
end
self.x = @character.screen_x
self.y = @character.screen_y
self.z = @character.screen_z(@ch)
# self.zoom_x = Game_Map::TILE_WIDTH / 32.0
# self.zoom_y = Game_Map::TILE_HEIGHT / 32.0
self.opacity = @character.opacity
this_x = @character.screen_x
this_x = ((this_x - (Graphics.width / 2)) * TilemapRenderer::ZOOM_X) + (Graphics.width / 2) if TilemapRenderer::ZOOM_X != 1
self.x = this_x
this_y = @character.screen_y
this_y = ((this_y - (Graphics.height / 2)) * TilemapRenderer::ZOOM_Y) + (Graphics.height / 2) if TilemapRenderer::ZOOM_Y != 1
self.y = this_y
self.z = @character.screen_z(@ch)
self.opacity = @character.opacity
self.blend_type = @character.blend_type
# self.bush_depth = @character.bush_depth
if @character.animation_id != 0
if @character.animation_id && @character.animation_id != 0
animation = $data_animations[@character.animation_id]
animation(animation, true)
animation(animation, true, @character.animation_height || 3, @character.animation_regular_tone || false)
@character.animation_id = 0
end
@reflection.update if @reflection
@surfbase.update if @surfbase
@reflection&.update
@surfbase&.update
end
end

View File

@@ -1,18 +1,18 @@
#===============================================================================
#
#===============================================================================
class Sprite_Reflection
attr_reader :visible
attr_accessor :event
def initialize(sprite,event,viewport=nil)
@rsprite = sprite
@sprite = nil
@event = event
@height = 0
def initialize(parent_sprite, viewport = nil)
@parent_sprite = parent_sprite
@sprite = nil
@height = 0
@fixedheight = false
if @event && @event!=$game_player
if @event.name[/reflection\((\d+)\)/i]
@height = $~[1].to_i || 0
@fixedheight = true
end
if @parent_sprite.character && @parent_sprite.character != $game_player &&
@parent_sprite.character.name[/reflection\((\d+)\)/i]
@height = $~[1].to_i || 0
@fixedheight = true
end
@viewport = viewport
@disposed = false
@@ -20,15 +20,19 @@ class Sprite_Reflection
end
def dispose
if !@disposed
@sprite.dispose if @sprite
@sprite = nil
@disposed = true
end
return if @disposed
@sprite&.dispose
@sprite = nil
@parent_sprite = nil
@disposed = true
end
def disposed?
@disposed
return @disposed
end
def event
return @parent_sprite.character
end
def visible=(value)
@@ -38,7 +42,7 @@ class Sprite_Reflection
def update
return if disposed?
shouldShow = @rsprite.visible
shouldShow = @parent_sprite.visible
if !shouldShow
# Just-in-time disposal of sprite
if @sprite
@@ -50,37 +54,40 @@ class Sprite_Reflection
# Just-in-time creation of sprite
@sprite = Sprite.new(@viewport) if !@sprite
if @sprite
x = @rsprite.x-@rsprite.ox
y = @rsprite.y-@rsprite.oy
y -= 32 if @rsprite.character.character_name[/offset/i]
x = @parent_sprite.x - (@parent_sprite.ox * TilemapRenderer::ZOOM_X)
y = @parent_sprite.y - (@parent_sprite.oy * TilemapRenderer::ZOOM_Y)
y -= Game_Map::TILE_HEIGHT * TilemapRenderer::ZOOM_Y if event.character_name[/offset/i]
@height = $PokemonGlobal.bridge if !@fixedheight
y += @height*16
width = @rsprite.src_rect.width
height = @rsprite.src_rect.height
@sprite.x = x+width/2
@sprite.y = y+height+height/2
@sprite.ox = width/2
@sprite.oy = height/2-2 # Hard-coded 2 pixel shift up
@sprite.oy -= @rsprite.character.bob_height*2
@sprite.z = -50 # Still water is -100, map is 0 and above
@sprite.zoom_x = @rsprite.zoom_x
@sprite.zoom_y = @rsprite.zoom_y
frame = (Graphics.frame_count%40)/10
@sprite.zoom_x *= [1.0, 0.95, 1.0, 1.05][frame]
y += @height * TilemapRenderer::ZOOM_Y * Game_Map::TILE_HEIGHT / 2
width = @parent_sprite.src_rect.width
height = @parent_sprite.src_rect.height
@sprite.x = x + ((width / 2) * TilemapRenderer::ZOOM_X)
@sprite.y = y + ((height + (height / 2)) * TilemapRenderer::ZOOM_Y)
@sprite.ox = width / 2
@sprite.oy = (height / 2) - 2 # Hard-coded 2 pixel shift up
@sprite.oy -= event.bob_height * 2
@sprite.z = @parent_sprite.groundY - (Graphics.height / 2)
@sprite.z -= 1000 # Still water is -2000, map is 0 and above
@sprite.z += 1 if event == $game_player
@sprite.zoom_x = @parent_sprite.zoom_x
if Settings::ANIMATE_REFLECTIONS && !GameData::MapMetadata.try_get(event.map_id)&.still_reflections
@sprite.zoom_x += 0.05 * @sprite.zoom_x * Math.sin(2 * Math::PI * System.uptime)
end
@sprite.zoom_y = @parent_sprite.zoom_y
@sprite.angle = 180.0
@sprite.mirror = true
@sprite.bitmap = @rsprite.bitmap
@sprite.tone = @rsprite.tone
if @height>0
@sprite.color = Color.new(48,96,160,255) # Dark still water
@sprite.opacity = @rsprite.opacity
@sprite.bitmap = @parent_sprite.bitmap
@sprite.tone = @parent_sprite.tone
if @height > 0
@sprite.color = Color.new(48, 96, 160, 255) # Dark still water
@sprite.opacity = @parent_sprite.opacity
@sprite.visible = !Settings::TIME_SHADING # Can't time-tone a colored sprite
else
@sprite.color = Color.new(224,224,224,96)
@sprite.opacity = @rsprite.opacity*3/4
@sprite.color = Color.new(224, 224, 224, 96)
@sprite.opacity = @parent_sprite.opacity * 3 / 4
@sprite.visible = true
end
@sprite.src_rect = @rsprite.src_rect
@sprite.src_rect = @parent_sprite.src_rect
end
end
end

View File

@@ -1,35 +1,41 @@
#===============================================================================
#
#===============================================================================
class Sprite_SurfBase
attr_reader :visible
attr_accessor :event
attr_reader :visible
def initialize(sprite,event,viewport=nil)
@rsprite = sprite
@sprite = nil
@event = event
def initialize(parent_sprite, viewport = nil)
@parent_sprite = parent_sprite
@sprite = nil
@viewport = viewport
@disposed = false
@surfbitmap = AnimatedBitmap.new("Graphics/Characters/base_surf")
@divebitmap = AnimatedBitmap.new("Graphics/Characters/base_dive")
RPG::Cache.retain("Graphics/Characters/base_surf")
RPG::Cache.retain("Graphics/Characters/base_dive")
@cws = @surfbitmap.width/4
@chs = @surfbitmap.height/4
@cwd = @divebitmap.width/4
@chd = @divebitmap.height/4
@cws = @surfbitmap.width / 4
@chs = @surfbitmap.height / 4
@cwd = @divebitmap.width / 4
@chd = @divebitmap.height / 4
update
end
def dispose
return if @disposed
@sprite.dispose if @sprite
@sprite = nil
@sprite&.dispose
@sprite = nil
@parent_sprite = nil
@surfbitmap.dispose
@divebitmap.dispose
@disposed = true
end
def disposed?
@disposed
return @disposed
end
def event
return @parent_sprite.character
end
def visible=(value)
@@ -49,35 +55,40 @@ class Sprite_SurfBase
end
# Just-in-time creation of sprite
@sprite = Sprite.new(@viewport) if !@sprite
if @sprite
if $PokemonGlobal.surfing
@sprite.bitmap = @surfbitmap.bitmap
cw = @cws
ch = @chs
elsif $PokemonGlobal.diving
@sprite.bitmap = @divebitmap.bitmap
cw = @cwd
ch = @chd
end
sx = @event.pattern_surf*cw
sy = ((@event.direction-2)/2)*ch
@sprite.src_rect.set(sx,sy,cw,ch)
if $PokemonTemp.surfJump
@sprite.x = ($PokemonTemp.surfJump[0]*Game_Map::REAL_RES_X-@event.map.display_x+3)/4+(Game_Map::TILE_WIDTH/2)
@sprite.y = ($PokemonTemp.surfJump[1]*Game_Map::REAL_RES_Y-@event.map.display_y+3)/4+(Game_Map::TILE_HEIGHT/2)+16
else
@sprite.x = @rsprite.x
@sprite.y = @rsprite.y
end
@sprite.ox = cw/2
@sprite.oy = ch-16 # Assume base needs offsetting
@sprite.oy -= @event.bob_height
@sprite.z = @event.screen_z(ch)-1
@sprite.zoom_x = @rsprite.zoom_x
@sprite.zoom_y = @rsprite.zoom_y
@sprite.tone = @rsprite.tone
@sprite.color = @rsprite.color
@sprite.opacity = @rsprite.opacity
return if !@sprite
if $PokemonGlobal.surfing
@sprite.bitmap = @surfbitmap.bitmap
cw = @cws
ch = @chs
elsif $PokemonGlobal.diving
@sprite.bitmap = @divebitmap.bitmap
cw = @cwd
ch = @chd
end
sx = event.pattern_surf * cw
sy = ((event.direction - 2) / 2) * ch
@sprite.src_rect.set(sx, sy, cw, ch)
if $game_temp.surf_base_coords
spr_x = ((($game_temp.surf_base_coords[0] * Game_Map::REAL_RES_X) - event.map.display_x).to_f / Game_Map::X_SUBPIXELS).round
spr_x += (Game_Map::TILE_WIDTH / 2)
spr_x = ((spr_x - (Graphics.width / 2)) * TilemapRenderer::ZOOM_X) + (Graphics.width / 2) if TilemapRenderer::ZOOM_X != 1
@sprite.x = spr_x
spr_y = ((($game_temp.surf_base_coords[1] * Game_Map::REAL_RES_Y) - event.map.display_y).to_f / Game_Map::Y_SUBPIXELS).round
spr_y += (Game_Map::TILE_HEIGHT / 2) + 16
spr_y = ((spr_y - (Graphics.height / 2)) * TilemapRenderer::ZOOM_Y) + (Graphics.height / 2) if TilemapRenderer::ZOOM_Y != 1
@sprite.y = spr_y
else
@sprite.x = @parent_sprite.x
@sprite.y = @parent_sprite.y
end
@sprite.ox = cw / 2
@sprite.oy = ch - 16 # Assume base needs offsetting
@sprite.oy -= event.bob_height
@sprite.z = event.screen_z(ch) - 1
@sprite.zoom_x = @parent_sprite.zoom_x
@sprite.zoom_y = @parent_sprite.zoom_y
@sprite.tone = @parent_sprite.tone
@sprite.color = @parent_sprite.color
@sprite.opacity = @parent_sprite.opacity
end
end

View File

@@ -1,12 +1,19 @@
#===============================================================================
#
#===============================================================================
class Spriteset_Global
attr_reader :playersprite
@@viewport2 = Viewport.new(0, 0, Settings::SCREEN_WIDTH, Settings::SCREEN_HEIGHT)
@@viewport2.z = 200
def initialize
@map_id = $game_map&.map_id || 0
@follower_sprites = FollowerSprites.new(Spriteset_Map.viewport)
@playersprite = Sprite_Character.new(Spriteset_Map.viewport, $game_player)
@weather = RPG::Weather.new(Spriteset_Map.viewport)
@picture_sprites = []
for i in 1..100
(1..100).each do |i|
@picture_sprites.push(Sprite_Picture.new(@@viewport2, $game_screen.pictures[i]))
end
@timer_sprite = Sprite_Timer.new
@@ -14,16 +21,38 @@ class Spriteset_Global
end
def dispose
@follower_sprites.dispose
@follower_sprites = nil
@playersprite.dispose
@picture_sprites.each { |sprite| sprite.dispose }
@timer_sprite.dispose
@playersprite = nil
@weather.dispose
@weather = nil
@picture_sprites.each { |sprite| sprite.dispose }
@picture_sprites.clear
@timer_sprite.dispose
@timer_sprite = nil
end
def update
@follower_sprites.update
@playersprite.update
if @weather.type != $game_screen.weather_type
@weather.fade_in($game_screen.weather_type, $game_screen.weather_max, $game_screen.weather_duration)
end
if @map_id != $game_map.map_id
offsets = $map_factory.getRelativePos(@map_id, 0, 0, $game_map.map_id, 0, 0)
if offsets == [0, 0]
@weather.ox_offset = 0
@weather.oy_offset = 0
else
@weather.ox_offset += offsets[0] * Game_Map::TILE_WIDTH
@weather.oy_offset += offsets[1] * Game_Map::TILE_HEIGHT
end
@map_id = $game_map.map_id
end
@weather.ox = ($game_map.display_x / Game_Map::X_SUBPIXELS).round
@weather.oy = ($game_map.display_y / Game_Map::Y_SUBPIXELS).round
@weather.update
@picture_sprites.each { |sprite| sprite.update }
@timer_sprite.update
end

View File

@@ -1,38 +1,42 @@
#===============================================================================
# Unused.
#===============================================================================
class ClippableSprite < Sprite_Character
def initialize(viewport,event,tilemap)
def initialize(viewport, event, tilemap)
@tilemap = tilemap
@_src_rect = Rect.new(0,0,0,0)
super(viewport,event)
@_src_rect = Rect.new(0, 0, 0, 0)
super(viewport, event)
end
def update
super
@_src_rect = self.src_rect
tmright = @tilemap.map_data.xsize*Game_Map::TILE_WIDTH-@tilemap.ox
echoln("x=#{self.x},ox=#{self.ox},tmright=#{tmright},tmox=#{@tilemap.ox}")
if @tilemap.ox-self.ox<-self.x
tmright = (@tilemap.map_data.xsize * Game_Map::TILE_WIDTH) - @tilemap.ox
echoln "x=#{self.x},ox=#{self.ox},tmright=#{tmright},tmox=#{@tilemap.ox}"
if @tilemap.ox - self.ox < -self.x
# clipped on left
diff = -self.x-@tilemap.ox+self.ox
self.src_rect = Rect.new(@_src_rect.x+diff,@_src_rect.y,
@_src_rect.width-diff,@_src_rect.height)
echoln("clipped out left: #{diff} #{@tilemap.ox-self.ox} #{self.x}")
elsif tmright-self.ox<self.x
diff = -self.x - @tilemap.ox + self.ox
self.src_rect = Rect.new(@_src_rect.x + diff, @_src_rect.y,
@_src_rect.width - diff, @_src_rect.height)
echoln "clipped out left: #{diff} #{@tilemap.ox - self.ox} #{self.x}"
elsif tmright - self.ox < self.x
# clipped on right
diff = self.x-tmright+self.ox
self.src_rect = Rect.new(@_src_rect.x,@_src_rect.y,
@_src_rect.width-diff,@_src_rect.height)
echoln("clipped out right: #{diff} #{tmright+self.ox} #{self.x}")
diff = self.x - tmright + self.ox
self.src_rect = Rect.new(@_src_rect.x, @_src_rect.y,
@_src_rect.width - diff, @_src_rect.height)
echoln "clipped out right: #{diff} #{tmright + self.ox} #{self.x}"
else
echoln("-not- clipped out left: #{diff} #{@tilemap.ox-self.ox} #{self.x}")
echoln "-not- clipped out left: #{diff} #{@tilemap.ox - self.ox} #{self.x}"
end
end
end
#===============================================================================
#
#===============================================================================
class Spriteset_Map
attr_reader :map
attr_accessor :tilemap
@@viewport0 = Viewport.new(0, 0, Settings::SCREEN_WIDTH, Settings::SCREEN_HEIGHT) # Panorama
@@viewport0.z = -100
@@viewport1 = Viewport.new(0, 0, Settings::SCREEN_WIDTH, Settings::SCREEN_HEIGHT) # Map, events, player, fog
@@ -40,51 +44,40 @@ class Spriteset_Map
@@viewport3 = Viewport.new(0, 0, Settings::SCREEN_WIDTH, Settings::SCREEN_HEIGHT) # Flashing
@@viewport3.z = 500
def Spriteset_Map.viewport # For access by Spriteset_Global
# For access by Spriteset_Global.
def self.viewport
return @@viewport1
end
def initialize(map=nil)
def initialize(map = nil)
@map = (map) ? map : $game_map
@tilemap = TilemapLoader.new(@@viewport1)
@tilemap.tileset = pbGetTileset(@map.tileset_name)
for i in 0...7
autotile_name = @map.autotile_names[i]
@tilemap.autotiles[i] = pbGetAutotile(autotile_name)
end
@tilemap.map_data = @map.data
@tilemap.priorities = @map.priorities
@tilemap.terrain_tags = @map.terrain_tags
$scene.map_renderer.add_tileset(@map.tileset_name)
@map.autotile_names.each { |filename| $scene.map_renderer.add_autotile(filename) }
$scene.map_renderer.add_extra_autotiles(@map.tileset_id)
@panorama = AnimatedPlane.new(@@viewport0)
@fog = AnimatedPlane.new(@@viewport1)
@fog.z = 3000
@character_sprites = []
for i in @map.events.keys.sort
sprite = Sprite_Character.new(@@viewport1,@map.events[i])
@map.events.keys.sort.each do |i|
sprite = Sprite_Character.new(@@viewport1, @map.events[i])
@character_sprites.push(sprite)
end
@weather = RPG::Weather.new(@@viewport1)
pbOnSpritesetCreate(self,@@viewport1)
EventHandlers.trigger(:on_new_spriteset_map, self, @@viewport1)
update
end
def dispose
@tilemap.tileset.dispose
for i in 0...7
@tilemap.autotiles[i].dispose
if $scene.is_a?(Scene_Map)
$scene.map_renderer.remove_tileset(@map.tileset_name)
@map.autotile_names.each { |filename| $scene.map_renderer.remove_autotile(filename) }
$scene.map_renderer.remove_extra_autotiles(@map.tileset_id)
end
@tilemap.dispose
@panorama.dispose
@fog.dispose
for sprite in @character_sprites
sprite.dispose
end
@weather.dispose
@tilemap = nil
@character_sprites.each { |sprite| sprite.dispose }
@panorama = nil
@fog = nil
@character_sprites.clear
@weather = nil
end
def getAnimations
@@ -96,51 +89,40 @@ class Spriteset_Map
end
def update
if @panorama_name!=@map.panorama_name || @panorama_hue!=@map.panorama_hue
if @panorama_name != @map.panorama_name || @panorama_hue != @map.panorama_hue
@panorama_name = @map.panorama_name
@panorama_hue = @map.panorama_hue
@panorama.setPanorama(nil) if @panorama.bitmap!=nil
@panorama.setPanorama(@panorama_name,@panorama_hue) if @panorama_name!=""
@panorama.set_panorama(nil) if !@panorama.bitmap.nil?
@panorama.set_panorama(@panorama_name, @panorama_hue) if !nil_or_empty?(@panorama_name)
Graphics.frame_reset
end
if @fog_name!=@map.fog_name || @fog_hue!=@map.fog_hue
if @fog_name != @map.fog_name || @fog_hue != @map.fog_hue
@fog_name = @map.fog_name
@fog_hue = @map.fog_hue
@fog.setFog(nil) if @fog.bitmap!=nil
@fog.setFog(@fog_name,@fog_hue) if @fog_name!=""
@fog.set_fog(nil) if !@fog.bitmap.nil?
@fog.set_fog(@fog_name, @fog_hue) if !nil_or_empty?(@fog_name)
Graphics.frame_reset
end
tmox = (@map.display_x/Game_Map::X_SUBPIXELS).round
tmoy = (@map.display_y/Game_Map::Y_SUBPIXELS).round
@tilemap.ox = tmox
@tilemap.oy = tmoy
@@viewport1.rect.set(0,0,Graphics.width,Graphics.height)
tmox = (@map.display_x / Game_Map::X_SUBPIXELS).round
tmoy = (@map.display_y / Game_Map::Y_SUBPIXELS).round
@@viewport1.rect.set(0, 0, Graphics.width, Graphics.height)
@@viewport1.ox = 0
@@viewport1.oy = 0
@@viewport1.ox += $game_screen.shake
@tilemap.update
@panorama.ox = tmox/2
@panorama.oy = tmoy/2
@fog.ox = tmox+@map.fog_ox
@fog.oy = tmoy+@map.fog_oy
@fog.zoom_x = @map.fog_zoom/100.0
@fog.zoom_y = @map.fog_zoom/100.0
@panorama.ox = tmox / 2
@panorama.oy = tmoy / 2
@fog.ox = tmox + @map.fog_ox
@fog.oy = tmoy + @map.fog_oy
@fog.zoom_x = @map.fog_zoom / 100.0
@fog.zoom_y = @map.fog_zoom / 100.0
@fog.opacity = @map.fog_opacity
@fog.blend_type = @map.fog_blend_type
@fog.tone = @map.fog_tone
@panorama.update
@fog.update
for sprite in @character_sprites
@character_sprites.each do |sprite|
sprite.update
end
if self.map!=$game_map
@weather.fade_in(:None, 0, 20)
else
@weather.fade_in($game_screen.weather_type, $game_screen.weather_max, $game_screen.weather_duration)
end
@weather.ox = tmox
@weather.oy = tmoy
@weather.update
@@viewport1.tone = $game_screen.tone
@@viewport3.color = $game_screen.flash_color
@@viewport1.update

View File

@@ -1,86 +1,336 @@
=begin
A sprite whose sole purpose is to display an animation. This sprite
can be displayed anywhere on the map and is disposed
automatically when its animation is finished.
Used for grass rustling and so forth.
=end
class AnimationSprite < RPG::Sprite
def initialize(animID,map,tileX,tileY,viewport=nil,tinting=false,height=3)
super(viewport)
@tileX = tileX
@tileY = tileY
self.bitmap = Bitmap.new(1, 1)
self.bitmap.clear
@map = map
setCoords
pbDayNightTint(self) if tinting
self.animation($data_animations[animID],true,height)
#===============================================================================
#
#===============================================================================
class SpriteAnimation
@@_animations = []
@@_reference_count = {}
def initialize(sprite)
@sprite = sprite
end
def setCoords
self.x = ((@tileX * Game_Map::REAL_RES_X - @map.display_x) / Game_Map::X_SUBPIXELS).ceil
self.x += Game_Map::TILE_WIDTH / 2
self.y = ((@tileY * Game_Map::REAL_RES_Y - @map.display_y) / Game_Map::Y_SUBPIXELS).ceil
self.y += Game_Map::TILE_HEIGHT
def x(*arg); @sprite.x(*arg); end
def y(*arg); @sprite.y(*arg); end
def ox(*arg); @sprite.ox(*arg); end
def oy(*arg); @sprite.oy(*arg); end
def viewport(*arg); @sprite.viewport(*arg); end
def flash(*arg); @sprite.flash(*arg); end
def src_rect(*arg); @sprite.src_rect(*arg); end
def opacity(*arg); @sprite.opacity(*arg); end
def tone(*arg); @sprite.tone(*arg); end
def self.clear
@@_animations.clear
end
def dispose
self.bitmap.dispose
super
dispose_animation
dispose_loop_animation
end
def animation(animation, hit, height = 3, no_tone = false)
dispose_animation
@_animation = animation
return if @_animation.nil?
@_animation_hit = hit
@_animation_height = height
@_animation_no_tone = no_tone
@_animation_duration = @_animation.frame_max
@_animation_index = -1
fr = 20
if @_animation.name[/\[\s*(\d+?)\s*\]\s*$/]
fr = $~[1].to_i
end
@_animation_time_per_frame = 1.0 / fr
@_animation_timer_start = System.uptime
animation_name = @_animation.animation_name
animation_hue = @_animation.animation_hue
bitmap = pbGetAnimation(animation_name, animation_hue)
if @@_reference_count.include?(bitmap)
@@_reference_count[bitmap] += 1
else
@@_reference_count[bitmap] = 1
end
@_animation_sprites = []
if @_animation.position != 3 || !@@_animations.include?(animation)
16.times do
sprite = ::Sprite.new(self.viewport)
sprite.bitmap = bitmap
sprite.visible = false
@_animation_sprites.push(sprite)
end
@@_animations.push(animation) unless @@_animations.include?(animation)
end
update_animation
end
def loop_animation(animation)
return if animation == @_loop_animation
dispose_loop_animation
@_loop_animation = animation
return if @_loop_animation.nil?
@_loop_animation_duration = @_animation.frame_max
@_loop_animation_index = -1
fr = 20
if @_animation.name[/\[\s*(\d+?)\s*\]\s*$/]
fr = $~[1].to_i
end
@_loop_animation_time_per_frame = 1.0 / fr
@_loop_animation_timer_start = System.uptime
animation_name = @_loop_animation.animation_name
animation_hue = @_loop_animation.animation_hue
bitmap = pbGetAnimation(animation_name, animation_hue)
if @@_reference_count.include?(bitmap)
@@_reference_count[bitmap] += 1
else
@@_reference_count[bitmap] = 1
end
@_loop_animation_sprites = []
16.times do
sprite = ::Sprite.new(self.viewport)
sprite.bitmap = bitmap
sprite.visible = false
@_loop_animation_sprites.push(sprite)
end
update_loop_animation
end
def dispose_animation
return if @_animation_sprites.nil?
sprite = @_animation_sprites[0]
if sprite
@@_reference_count[sprite.bitmap] -= 1
sprite.bitmap.dispose if @@_reference_count[sprite.bitmap] == 0
end
@_animation_sprites.each { |s| s.dispose }
@_animation_sprites = nil
@_animation = nil
@_animation_duration = 0
end
def dispose_loop_animation
return if @_loop_animation_sprites.nil?
sprite = @_loop_animation_sprites[0]
if sprite
@@_reference_count[sprite.bitmap] -= 1
sprite.bitmap.dispose if @@_reference_count[sprite.bitmap] == 0
end
@_loop_animation_sprites.each { |s| s.dispose }
@_loop_animation_sprites = nil
@_loop_animation = nil
end
def active?
return @_loop_animation_sprites || @_animation_sprites
end
def effect?
return @_animation_duration > 0
end
def update
if !self.disposed?
setCoords
super
self.dispose if !self.effect?
update_animation if @_animation
update_loop_animation if @_loop_animation
end
def update_animation
new_index = ((System.uptime - @_animation_timer_start) / @_animation_time_per_frame).to_i
if new_index >= @_animation_duration
dispose_animation
return
end
quick_update = (@_animation_index == new_index)
@_animation_index = new_index
frame_index = @_animation_index
cell_data = @_animation.frames[frame_index].cell_data
position = @_animation.position
animation_set_sprites(@_animation_sprites, cell_data, position, quick_update)
return if quick_update
@_animation.timings.each do |timing|
next if timing.frame != frame_index
animation_process_timing(timing, @_animation_hit)
end
end
def update_loop_animation
new_index = ((System.uptime - @_loop_animation_timer_start) / @_loop_animation_time_per_frame).to_i
new_index %= @_loop_animation_duration
quick_update = (@_loop_animation_index == new_index)
@_loop_animation_index = new_index
frame_index = @_loop_animation_index
cell_data = @_loop_animation.frames[frame_index].cell_data
position = @_loop_animation.position
animation_set_sprites(@_loop_animation_sprites, cell_data, position, quick_update)
return if quick_update
@_loop_animation.timings.each do |timing|
next if timing.frame != frame_index
animation_process_timing(timing, true)
end
end
def animation_set_sprites(sprites, cell_data, position, quick_update = false)
sprite_x = 320
sprite_y = 240
if position == 3 # Screen
if self.viewport
sprite_x = self.viewport.rect.width / 2
sprite_y = self.viewport.rect.height - 160
end
else
sprite_x = self.x - self.ox + (self.src_rect.width / 2)
sprite_y = self.y - self.oy
if self.src_rect.height > 1
sprite_y += self.src_rect.height / 2 if position == 1 # Middle
sprite_y += self.src_rect.height if position == 2 # Bottom
end
end
16.times do |i|
sprite = sprites[i]
pattern = cell_data[i, 0]
if sprite.nil? || pattern.nil? || pattern == -1
sprite.visible = false if sprite
next
end
sprite.x = sprite_x + cell_data[i, 1]
sprite.y = sprite_y + cell_data[i, 2]
next if quick_update
sprite.visible = true
sprite.src_rect.set((pattern % 5) * 192, (pattern / 5) * 192, 192, 192)
case @_animation_height
when -1 then sprite.z = -25
when 0 then sprite.z = 1
when 1 then sprite.z = sprite.y + (Game_Map::TILE_HEIGHT * 3 / 2) + 1
when 2 then sprite.z = sprite.y + (Game_Map::TILE_HEIGHT * 3) + 1
else sprite.z = 2000
end
sprite.ox = 96
sprite.oy = 96
sprite.zoom_x = cell_data[i, 3] / 100.0
sprite.zoom_y = cell_data[i, 3] / 100.0
sprite.angle = cell_data[i, 4]
sprite.mirror = (cell_data[i, 5] == 1)
sprite.tone = self.tone if !@_animation_no_tone
sprite.opacity = cell_data[i, 6] * self.opacity / 255.0
sprite.blend_type = cell_data[i, 7]
end
end
def animation_process_timing(timing, hit)
if timing.condition == 0 ||
(timing.condition == 1 && hit == true) ||
(timing.condition == 2 && hit == false)
if timing.se.name != ""
se = timing.se
pbSEPlay(se)
end
case timing.flash_scope
when 1
self.flash(timing.flash_color, timing.flash_duration * 2)
when 2
self.viewport.flash(timing.flash_color, timing.flash_duration * 2) if self.viewport
when 3
self.flash(nil, timing.flash_duration * 2)
end
end
end
def x=(x)
sx = x - self.x
return if sx == 0
if @_animation_sprites
16.times { |i| @_animation_sprites[i].x += sx }
end
if @_loop_animation_sprites
16.times { |i| @_loop_animation_sprites[i].x += sx }
end
end
def y=(y)
sy = y - self.y
return if sy == 0
if @_animation_sprites
16.times { |i| @_animation_sprites[i].y += sy }
end
if @_loop_animation_sprites
16.times { |i| @_loop_animation_sprites[i].y += sy }
end
end
end
#===============================================================================
# A sprite whose sole purpose is to display an animation (a SpriteAnimation).
# This sprite can be displayed anywhere on the map and is disposed automatically
# when its animation is finished. Used for grass rustling and so forth.
#===============================================================================
class AnimationContainerSprite < RPG::Sprite
def initialize(animID, map, tileX, tileY, viewport = nil, tinting = false, height = 3)
super(viewport)
@tileX = tileX
@tileY = tileY
@map = map
setCoords
pbDayNightTint(self) if tinting
self.animation($data_animations[animID], true, height)
end
def setCoords
self.x = (((@tileX * Game_Map::REAL_RES_X) - @map.display_x) / Game_Map::X_SUBPIXELS).ceil
self.x += Game_Map::TILE_WIDTH / 2
self.y = (((@tileY * Game_Map::REAL_RES_Y) - @map.display_y) / Game_Map::Y_SUBPIXELS).ceil
self.y += Game_Map::TILE_HEIGHT
end
def update
return if disposed?
setCoords
super
dispose if !effect?
end
end
#===============================================================================
#
#===============================================================================
class Spriteset_Map
alias _animationSprite_initialize initialize
alias _animationSprite_update update
alias _animationSprite_dispose dispose
attr_reader :usersprites
def initialize(map=nil)
@usersprites=[]
alias _animationSprite_initialize initialize unless private_method_defined?(:_animationSprite_initialize)
alias _animationSprite_update update unless method_defined?(:_animationSprite_update)
alias _animationSprite_dispose dispose unless method_defined?(:_animationSprite_dispose)
def initialize(map = nil)
@usersprites = []
_animationSprite_initialize(map)
end
def addUserAnimation(animID,x,y,tinting=false,height=3)
sprite=AnimationSprite.new(animID,$game_map,x,y,@@viewport1,tinting,height)
# Used to display animations that remain in the same location on the map.
# Typically for grass rustling and dust clouds, and other animations that
# aren't relative to an event.
def addUserAnimation(animID, x, y, tinting = false, height = 3)
sprite = AnimationContainerSprite.new(animID, self.map, x, y, @@viewport1, tinting, height)
addUserSprite(sprite)
return sprite
end
def addUserSprite(sprite)
for i in 0...@usersprites.length
if @usersprites[i]==nil || @usersprites[i].disposed?
@usersprites[i]=sprite
return
end
def addUserSprite(new_sprite)
@usersprites.each_with_index do |sprite, i|
next if sprite && !sprite.disposed?
@usersprites[i] = new_sprite
return
end
@usersprites.push(sprite)
@usersprites.push(new_sprite)
end
def dispose
_animationSprite_dispose
for i in 0...@usersprites.length
@usersprites[i].dispose
end
@usersprites.each { |sprite| sprite.dispose }
@usersprites.clear
end
def update
return if @tilemap.disposed?
pbDayNightTint(@tilemap)
@@viewport3.tone.set(0,0,0,0)
@@viewport3.tone.set(0, 0, 0, 0)
_animationSprite_update
for i in 0...@usersprites.length
@usersprites[i].update if !@usersprites[i].disposed?
end
@usersprites.each { |sprite| sprite.update if !sprite.disposed? }
@usersprites.delete_if { |sprite| sprite.disposed? }
end
end

View File

@@ -7,19 +7,19 @@
class Sprite_Shadow < RPG::Sprite
attr_accessor :character
def initialize(viewport, character = nil,params=[])
def initialize(viewport, character = nil, params = [])
super(viewport)
@source = params[0]
@anglemin = (params.size>1) ? params[1] : 0
@anglemax = (params.size>2) ? params[2] : 0
@self_opacity = (params.size>4) ? params[4] : 100
@distancemax = (params.size>3) ? params[3] : 350
@anglemin = (params.size > 1) ? params[1] : 0
@anglemax = (params.size > 2) ? params[2] : 0
@self_opacity = (params.size > 4) ? params[4] : 100
@distancemax = (params.size > 3) ? params[3] : 350
@character = character
update
end
def dispose
@chbitmap.dispose if @chbitmap
@chbitmap&.dispose
super
end
@@ -35,19 +35,18 @@ class Sprite_Shadow < RPG::Sprite
@tile_id = @character.tile_id
@character_name = @character.character_name
@character_hue = @character.character_hue
@chbitmap&.dispose
if @tile_id >= 384
@chbitmap.dispose if @chbitmap
@chbitmap = pbGetTileBitmap(@character.map.tileset_name,
@tile_id, @character.character_hue)
@tile_id, @character.character_hue)
self.src_rect.set(0, 0, 32, 32)
@ch = 32
@cw = 32
self.ox = 16
self.oy = 32
else
@chbitmap.dispose if @chbitmap
@chbitmap = AnimatedBitmap.new(
'Graphics/Characters/'+@character.character_name,@character.character_hue)
@chbitmap = AnimatedBitmap.new("Graphics/Characters/" + @character.character_name,
@character.character_hue)
@cw = @chbitmap.width / 4
@ch = @chbitmap.height / 4
self.ox = @cw / 2
@@ -75,8 +74,8 @@ class Sprite_Shadow < RPG::Sprite
self.src_rect.set(sx, sy, @cw, @ch)
end
self.x = ScreenPosHelper.pbScreenX(@character)
self.y = ScreenPosHelper.pbScreenY(@character)-5
self.z = ScreenPosHelper.pbScreenZ(@character,@ch)-1
self.y = ScreenPosHelper.pbScreenY(@character) - 5
self.z = ScreenPosHelper.pbScreenZ(@character, @ch) - 1
self.zoom_x = ScreenPosHelper.pbScreenZoomX(@character)
self.zoom_y = ScreenPosHelper.pbScreenZoomY(@character)
self.blend_type = @character.blend_type
@@ -88,11 +87,11 @@ class Sprite_Shadow < RPG::Sprite
end
@deltax = ScreenPosHelper.pbScreenX(@source) - self.x
@deltay = ScreenPosHelper.pbScreenY(@source) - self.y
self.color = Color.new(0, 0, 0)
@distance = ((@deltax ** 2) + (@deltay ** 2))
self.opacity = @self_opacity * 13000 / ((@distance * 370 / @distancemax) + 6000)
self.color = Color.black
@distance = ((@deltax**2) + (@deltay**2))
self.opacity = @self_opacity * 13_000 / ((@distance * 370 / @distancemax) + 6000)
self.angle = 57.3 * Math.atan2(@deltax, @deltay)
@angle_trigo = self.angle+90
@angle_trigo = self.angle + 90
@angle_trigo += 360 if @angle_trigo < 0
if @anglemin != 0 || @anglemax != 0
if (@angle_trigo < @anglemin || @angle_trigo > @anglemax) && @anglemin < @anglemax
@@ -106,7 +105,8 @@ class Sprite_Shadow < RPG::Sprite
end
end
def in_range?(element, object, range) # From Near's Anti Lag Script, edited
# From Near's Anti Lag Script, edited.
def in_range?(element, object, range)
elemScreenX = ScreenPosHelper.pbScreenX(element)
elemScreenY = ScreenPosHelper.pbScreenY(element)
objScreenX = ScreenPosHelper.pbScreenX(object)
@@ -118,13 +118,11 @@ class Sprite_Shadow < RPG::Sprite
end
end
#===================================================
#===============================================================================
# ? CLASS Sprite_Character edit
#===================================================
#===============================================================================
class Sprite_Character < RPG::Sprite
alias :shadow_initialize :initialize
alias shadow_initialize initialize unless private_method_defined?(:shadow_initialize)
def initialize(viewport, character = nil)
@ombrelist = []
@@ -132,84 +130,74 @@ class Sprite_Character < RPG::Sprite
shadow_initialize(viewport, @character)
end
def setShadows(map,shadows)
def setShadows(map, shadows)
if character.is_a?(Game_Event) && shadows.length > 0
params = XPML_read(map,"Shadow",@character,4)
if params != nil
for i in 0...shadows.size
@ombrelist.push(Sprite_Shadow.new(viewport, @character, shadows[i]))
params = XPML_read(map, "Shadow", @character, 4)
if params
shadows.each do |shadow|
@ombrelist.push(Sprite_Shadow.new(viewport, @character, shadows))
end
end
end
if character.is_a?(Game_Player) && shadows.length > 0
for i in 0...shadows.size
@ombrelist.push(Sprite_Shadow.new(viewport, $game_player, shadows[i]))
shadows.each do |shadow|
@ombrelist.push(Sprite_Shadow.new(viewport, $game_player, shadow))
end
end
update
end
def clearShadows
@ombrelist.each { |s| s.dispose if s }
@ombrelist.each { |s| s&.dispose }
@ombrelist.clear
end
alias shadow_update update
alias shadow_update update unless method_defined?(:shadow_update)
def update
shadow_update
if @ombrelist.length>0
for i in 0...@ombrelist.size
@ombrelist[i].update
end
end
@ombrelist.each { |ombre| ombre.update }
end
end
#===================================================
#===============================================================================
# ? CLASS Game_Event edit
#===================================================
#===============================================================================
class Game_Event
attr_accessor :id
end
#===================================================
#===============================================================================
# ? CLASS Spriteset_Map edit
#===================================================
#===============================================================================
class Spriteset_Map
attr_accessor :shadows
alias shadow_initialize initialize
def initialize(map=nil)
alias shadow_initialize initialize unless private_method_defined?(:shadow_initialize)
def initialize(map = nil)
@shadows = []
warn = false
map = $game_map if !map
for k in map.events.keys.sort
map.events.keys.sort.each do |k|
ev = map.events[k]
warn = true if (ev.list != nil && ev.list.length > 0 &&
ev.list[0].code == 108 &&
(ev.list[0].parameters == ["s"] || ev.list[0].parameters == ["o"]))
params = XPML_read(map,"Shadow Source", ev, 4)
@shadows.push([ev] + params) if params != nil
warn = true if ev.list && ev.list.length > 0 && ev.list[0].code == 108 &&
(ev.list[0].parameters == ["s"] || ev.list[0].parameters == ["o"])
params = XPML_read(map, "Shadow Source", ev, 4)
@shadows.push([ev] + params) if params
end
if warn == true
p "Warning : At least one event on this map uses the obsolete way to add shadows"
end
shadow_initialize(map)
for sprite in @character_sprites
@character_sprites.each do |sprite|
sprite.setShadows(map, @shadows)
end
$scene.spritesetGlobal.playersprite.setShadows(map, @shadows)
end
end
#===================================================
#===============================================================================
# ? XPML Definition, by Rataime, using ideas from Near Fantastica
#
# Returns nil if the markup wasn't present at all,
@@ -227,32 +215,25 @@ end
# p XPML_read("second", event_id) -> [1, "two"]
# p XPML_read("third", event_id) -> [3]
# p XPML_read("forth", event_id) -> nil
#===================================================
def XPML_read(map,markup,event,max_param_number=0)
#===============================================================================
def XPML_read(map, markup, event, max_param_number = 0)
parameter_list = nil
return nil if !event || event.list == nil
for i in 0...event.list.size
if event.list[i].code == 108 &&
event.list[i].parameters[0].downcase == "begin " + markup.downcase
parameter_list = [] if parameter_list == nil
for j in i+1...event.list.size
if event.list[j].code == 108
parts = event.list[j].parameters[0].split
if parts.size != 1 && parts[0].downcase != "begin"
if parts[1].to_i != 0 || parts[1] == "0"
parameter_list.push(parts[1].to_i)
else
parameter_list.push(parts[1])
end
else
return parameter_list
end
else
return parameter_list
end
return parameter_list if max_param_number != 0 && j == i + max_param_number
end
return nil if !event || event.list.nil?
event.list.size.times do |i|
next unless event.list[i].code == 108 &&
event.list[i].parameters[0].downcase == "begin " + markup.downcase
parameter_list = [] if parameter_list.nil?
((i + 1)...event.list.size).each do |j|
return parameter_list if event.list[j].code != 108
parts = event.list[j].parameters[0].split
return parameter_list if parts.size == 1 || parts[0].downcase == "begin"
if parts[1].to_i != 0 || parts[1] == "0"
parameter_list.push(parts[1].to_i)
else
parameter_list.push(parts[1])
end
return parameter_list if max_param_number != 0 && j == i + max_param_number
end
end
return parameter_list
end

View File

@@ -1,587 +0,0 @@
# Particle Engine, Peter O., 2007-11-03
# Based on version 2 by Near Fantastica, 04.01.06
# In turn based on the Particle Engine designed by PinkMan
class Particle_Engine
def initialize(viewport=nil,map=nil)
@map = (map) ? map : $game_map
@viewport = viewport
@effect = []
@disposed = false
@firsttime = true
@effects = {
# PinkMan's Effects
"fire" => Particle_Engine::Fire,
"smoke" => Particle_Engine::Smoke,
"teleport" => Particle_Engine::Teleport,
"spirit" => Particle_Engine::Spirit,
"explosion" => Particle_Engine::Explosion,
"aura" => Particle_Engine::Aura,
# BlueScope's Effects
"soot" => Particle_Engine::Soot,
"sootsmoke" => Particle_Engine::SootSmoke,
"rocket" => Particle_Engine::Rocket,
"fixteleport" => Particle_Engine::FixedTeleport,
"smokescreen" => Particle_Engine::Smokescreen,
"flare" => Particle_Engine::Flare,
"splash" => Particle_Engine::Splash,
# By Peter O.
"starteleport" => Particle_Engine::StarTeleport
}
end
def dispose
return if disposed?
for particle in @effect
next if particle.nil?
particle.dispose
end
@effect.clear
@map = nil
@disposed = true
end
def disposed?
return @disposed
end
def add_effect(event)
@effect[event.id] = pbParticleEffect(event)
end
def remove_effect(event)
return if @effect[event.id].nil?
@effect[event.id].dispose
@effect.delete_at(event.id)
end
def realloc_effect(event,particle)
type = pbEventCommentInput(event, 1, "Particle Engine Type")
if type.nil?
particle.dispose if particle
return nil
end
type = type[0].downcase
cls = @effects[type]
if cls.nil?
particle.dispose if particle
return nil
end
if !particle || !particle.is_a?(cls)
particle.dispose if particle
particle = cls.new(event,@viewport)
end
return particle
end
def pbParticleEffect(event)
return realloc_effect(event,nil)
end
def update
if @firsttime
@firsttime = false
for event in @map.events.values
remove_effect(event)
add_effect(event)
end
end
for i in 0...@effect.length
particle = @effect[i]
next if particle.nil?
if particle.event.pe_refresh
event = particle.event
event.pe_refresh = false
particle = realloc_effect(event,particle)
@effect[i] = particle
end
particle.update if particle
end
end
end
class ParticleEffect
attr_accessor :x, :y, :z
def initialize
@x = 0
@y = 0
@z = 0
end
def update; end
def dispose; end
end
class ParticleSprite
attr_accessor :x, :y, :z, :ox, :oy, :opacity, :blend_type
attr_reader :bitmap
def initialize(viewport)
@viewport = viewport
@sprite = nil
@x = 0
@y = 0
@z = 0
@ox = 0
@oy = 0
@opacity = 255
@bitmap = nil
@blend_type = 0
@minleft = 0
@mintop = 0
end
def dispose
@sprite.dispose if @sprite
end
def bitmap=(value)
@bitmap = value
if value
@minleft = -value.width
@mintop = -value.height
else
@minleft = 0
@mintop = 0
end
end
def update
w = Graphics.width
h = Graphics.height
if !@sprite && @x>=@minleft && @y>=@mintop && @x<w && @y<h
@sprite = Sprite.new(@viewport)
elsif @sprite && (@x<@minleft || @y<@mintop || @x>=w || @y>=h)
@sprite.dispose
@sprite = nil
end
if @sprite
@sprite.x = @x if @sprite.x!=@x
@sprite.x -= @ox
@sprite.y = @y if @sprite.y!=@y
@sprite.y -= @oy
@sprite.z = @z if @sprite.z!=@z
@sprite.opacity = @opacity if @sprite.opacity!=@opacity
@sprite.blend_type = @blend_type if @sprite.blend_type!=@blend_type
@sprite.bitmap = @bitmap if @sprite.bitmap!=@bitmap
end
end
end
class ParticleEffect_Event < ParticleEffect
attr_accessor :event
def initialize(event,viewport=nil)
@event = event
@viewport = viewport
@particles = []
@bitmaps = {}
end
def setParameters(params)
@randomhue,@leftright,@fade,
@maxparticless,@hue,@slowdown,
@ytop,@ybottom,@xleft,@xright,
@xgravity,@ygravity,@xoffset,@yoffset,
@opacityvar,@originalopacity = params
end
def loadBitmap(filename,hue)
key = [filename,hue]
bitmap = @bitmaps[key]
if !bitmap || bitmap.disposed?
bitmap = AnimatedBitmap.new("Graphics/Fogs/"+filename,hue).deanimate
@bitmaps[key] = bitmap
end
return bitmap
end
def initParticles(filename,opacity,zOffset=0,blendtype=1)
@particles = []
@particlex = []
@particley = []
@opacity = []
@startingx = self.x + @xoffset
@startingy = self.y + @yoffset
@screen_x = self.x
@screen_y = self.y
@real_x = @event.real_x
@real_y = @event.real_y
@filename = filename
@zoffset = zOffset
@bmwidth = 32
@bmheight = 32
for i in 0...@maxparticless
@particlex[i] = -@xoffset
@particley[i] = -@yoffset
@particles[i] = ParticleSprite.new(@viewport)
@particles[i].bitmap = loadBitmap(filename, @hue) if filename
if i==0 && @particles[i].bitmap
@bmwidth = @particles[i].bitmap.width
@bmheight = @particles[i].bitmap.height
end
@particles[i].blend_type = blendtype
@particles[i].y = @startingy
@particles[i].x = @startingx
@particles[i].z = self.z+zOffset
@opacity[i] = rand(opacity/4)
@particles[i].opacity = @opacity[i]
@particles[i].update
end
end
def x; return ScreenPosHelper.pbScreenX(@event); end
def y; return ScreenPosHelper.pbScreenY(@event); end
def z; return ScreenPosHelper.pbScreenZ(@event); end
def update
if @viewport &&
(@viewport.rect.x >= Graphics.width ||
@viewport.rect.y >= Graphics.height)
return
end
selfX = self.x
selfY = self.y
selfZ = self.z
newRealX = @event.real_x
newRealY = @event.real_y
@startingx = selfX + @xoffset
@startingy = selfY + @yoffset
@__offsetx = (@real_x==newRealX) ? 0 : selfX-@screen_x
@__offsety = (@real_y==newRealY) ? 0 : selfY-@screen_y
@screen_x = selfX
@screen_y = selfY
@real_x = newRealX
@real_y = newRealY
if @opacityvar>0 && @viewport
opac = 255.0/@opacityvar
minX = opac*(-@xgravity*1.0 / @slowdown).floor + @startingx
maxX = opac*(@xgravity*1.0 / @slowdown).floor + @startingx
minY = opac*(-@ygravity*1.0 / @slowdown).floor + @startingy
maxY = @startingy
minX -= @bmwidth
minY -= @bmheight
maxX += @bmwidth
maxY += @bmheight
if maxX<0 || maxY<0 || minX>=Graphics.width || minY>=Graphics.height
# echo "skipped"
return
end
end
particleZ = selfZ+@zoffset
for i in 0...@maxparticless
@particles[i].z = particleZ
if @particles[i].y <= @ytop
@particles[i].y = @startingy + @yoffset
@particles[i].x = @startingx + @xoffset
@particlex[i] = 0.0
@particley[i] = 0.0
end
if @particles[i].x <= @xleft
@particles[i].y = @startingy + @yoffset
@particles[i].x = @startingx + @xoffset
@particlex[i] = 0.0
@particley[i] = 0.0
end
if @particles[i].y >= @ybottom
@particles[i].y = @startingy + @yoffset
@particles[i].x = @startingx + @xoffset
@particlex[i] = 0.0
@particley[i] = 0.0
end
if @particles[i].x >= @xright
@particles[i].y = @startingy + @yoffset
@particles[i].x = @startingx + @xoffset
@particlex[i] = 0.0
@particley[i] = 0.0
end
if @fade == 0
if @opacity[i] <= 0
@opacity[i] = @originalopacity
@particles[i].y = @startingy + @yoffset
@particles[i].x = @startingx + @xoffset
@particlex[i] = 0.0
@particley[i] = 0.0
end
else
if @opacity[i] <= 0
@opacity[i] = 250
@particles[i].y = @startingy + @yoffset
@particles[i].x = @startingx + @xoffset
@particlex[i] = 0.0
@particley[i] = 0.0
end
end
calcParticlePos(i)
if @randomhue == 1
@hue += 0.5
@hue = 0 if @hue >= 360
@particles[i].bitmap = loadBitmap(@filename, @hue) if @filename
end
@opacity[i] = @opacity[i] - rand(@opacityvar)
@particles[i].opacity = @opacity[i]
@particles[i].update
end
end
def calcParticlePos(i)
@leftright = rand(2)
if @leftright == 1
xo = -@xgravity*1.0 / @slowdown
else
xo = @xgravity*1.0 / @slowdown
end
yo = -@ygravity*1.0 / @slowdown
@particlex[i] += xo
@particley[i] += yo
@particlex[i] -= @__offsetx
@particley[i] -= @__offsety
@particlex[i] = @particlex[i].floor
@particley[i] = @particley[i].floor
@particles[i].x = @particlex[i]+@startingx+@xoffset
@particles[i].y = @particley[i]+@startingy+@yoffset
end
def dispose
for particle in @particles
particle.dispose
end
for bitmap in @bitmaps.values
bitmap.dispose
end
@particles.clear
@bitmaps.clear
end
end
class Particle_Engine::Fire < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,1,20,40,0.5,-64,
Graphics.height,-64,Graphics.width,0.5,0.10,-5,-13,30,0])
initParticles("particle",250)
end
end
class Particle_Engine::Smoke < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,0,80,20,0.5,-64,
Graphics.height,-64,Graphics.width,0.5,0.10,-5,-15,5,80])
initParticles("smoke",250)
end
end
class Particle_Engine::Teleport < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([1,1,1,10,rand(360),1,-64,
Graphics.height,-64,Graphics.width,0,3,-8,-15,20,0])
initParticles("wideportal",250)
for i in 0...@maxparticless
@particles[i].ox = 16
@particles[i].oy = 16
end
end
end
class Particle_Engine::Spirit < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([1,0,1,20,rand(360),0.5,-64,
Graphics.height,-64,Graphics.width,0.5,0.10,-5,-13,30,0])
initParticles("particle",250)
end
end
class Particle_Engine::Explosion < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,1,20,0,0.5,-64,
Graphics.height,-64,Graphics.width,0.5,0.10,-5,-13,30,0])
initParticles("explosion",250)
end
end
class Particle_Engine::Aura < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,1,20,0,1,-64,
Graphics.height,-64,Graphics.width,2,2,-5,-13,30,0])
initParticles("particle",250)
end
end
class Particle_Engine::Soot < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,0,20,0,0.5,-64,
Graphics.height,-64,Graphics.width,0.5,0.10,-5,-15,5,80])
initParticles("smoke",100,0,2)
end
end
class Particle_Engine::SootSmoke < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,0,30,0,0.5,-64,
Graphics.height,-64,Graphics.width,0.5,0.10,-5,-15,5,80])
initParticles("smoke",100,0)
for i in 0...@maxparticless
@particles[i].blend_type = rand(6) < 3 ? 1 : 2
end
end
end
class Particle_Engine::Rocket < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,0,60,0,0.5,-64,
Graphics.height,-64,Graphics.width,0.5,0,-5,-15,5,80])
initParticles("smoke",100,-1)
end
end
class Particle_Engine::FixedTeleport < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([1,0,1,10,rand(360),1,
-Graphics.height,Graphics.height,0,Graphics.width,0,3,-8,-15,20,0])
initParticles("wideportal",250)
for i in 0...@maxparticless
@particles[i].ox = 16
@particles[i].oy = 16
end
end
end
# By Peter O.
class Particle_Engine::StarTeleport < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,1,10,0,1,
-Graphics.height,Graphics.height,0,Graphics.width,0,3,-8,-15,10,0])
initParticles("star",250)
for i in 0...@maxparticless
@particles[i].ox = 48
@particles[i].oy = 48
end
end
end
class Particle_Engine::Smokescreen < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,0,250,0,0.2,-64,
Graphics.height,-64,Graphics.width,0.8,0.8,-5,-15,5,80])
initParticles(nil,100)
for i in 0...@maxparticless
rnd = rand(3)
@opacity[i] = (rnd==0) ? 1 : 100
filename = (rnd==0) ? "explosionsmoke" : "smoke"
@particles[i].bitmap = loadBitmap(filename, @hue)
end
end
def calcParticlePos(i)
if @randomhue==1
filename = (rand(3)==0) ? "explosionsmoke" : "smoke"
@particles[i].bitmap = loadBitmap(filename, @hue)
end
multiple = 1.7
xgrav = @xgravity*multiple/@slowdown
xgrav = -xgrav if (rand(2)==1)
ygrav = @ygravity*multiple/@slowdown
ygrav = -ygrav if (rand(2)==1)
@particlex[i] += xgrav
@particley[i] += ygrav
@particlex[i] -= @__offsetx
@particley[i] -= @__offsety
@particlex[i] = @particlex[i].floor
@particley[i] = @particley[i].floor
@particles[i].x = @particlex[i]+@startingx+@xoffset
@particles[i].y = @particley[i]+@startingy+@yoffset
end
end
class Particle_Engine::Flare < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,1,30,10,1,-64,
Graphics.height,-64,Graphics.width,2,2,-5,-12,30,0])
initParticles("particle",255)
end
end
class Particle_Engine::Splash < ParticleEffect_Event
def initialize(event,viewport)
super
setParameters([0,0,1,30,255,1,-64,
Graphics.height,-64,Graphics.width,4,2,-5,-12,30,0])
initParticles("smoke",50)
end
def update
super
for i in 0...@maxparticless
@particles[i].opacity = 50
@particles[i].update
end
end
end
class Game_Event < Game_Character
attr_accessor :pe_refresh
alias nf_particles_game_map_initialize initialize
def initialize(map_id,event,map=nil)
@pe_refresh = false
begin
nf_particles_game_map_initialize(map_id, event, map)
rescue ArgumentError
nf_particles_game_map_initialize(map_id, event)
end
end
alias nf_particles_game_map_refresh refresh
def refresh
nf_particles_game_map_refresh
@pe_refresh = true
end
end

View File

@@ -0,0 +1,548 @@
#===============================================================================
#
#===============================================================================
class PictureOrigin
TOP_LEFT = 0
CENTER = 1
TOP_RIGHT = 2
BOTTOM_LEFT = 3
LOWER_LEFT = 3
BOTTOM_RIGHT = 4
LOWER_RIGHT = 4
TOP = 5
BOTTOM = 6
LEFT = 7
RIGHT = 8
end
#===============================================================================
#
#===============================================================================
class Processes
XY = 0
DELTA_XY = 1
Z = 2
CURVE = 3
ZOOM = 4
ANGLE = 5
TONE = 6
COLOR = 7
HUE = 8
OPACITY = 9
VISIBLE = 10
BLEND_TYPE = 11
SE = 12
NAME = 13
ORIGIN = 14
SRC = 15
SRC_SIZE = 16
CROP_BOTTOM = 17
end
#===============================================================================
#
#===============================================================================
def getCubicPoint2(src, t)
x0 = src[0]
y0 = src[1]
cx0 = src[2]
cy0 = src[3]
cx1 = src[4]
cy1 = src[5]
x1 = src[6]
y1 = src[7]
x1 = cx1 + ((x1 - cx1) * t)
x0 += ((cx0 - x0) * t)
cx0 += ((cx1 - cx0) * t)
cx1 = cx0 + ((x1 - cx0) * t)
cx0 = x0 + ((cx0 - x0) * t)
cx = cx0 + ((cx1 - cx0) * t)
# a = x1 - 3 * cx1 + 3 * cx0 - x0
# b = 3 * (cx1 - 2 * cx0 + x0)
# c = 3 * (cx0 - x0)
# d = x0
# cx = a*t*t*t + b*t*t + c*t + d
y1 = cy1 + ((y1 - cy1) * t)
y0 += ((cy0 - y0) * t)
cy0 += ((cy1 - cy0) * t)
cy1 = cy0 + ((y1 - cy0) * t)
cy0 = y0 + ((cy0 - y0) * t)
cy = cy0 + ((cy1 - cy0) * t)
# a = y1 - 3 * cy1 + 3 * cy0 - y0
# b = 3 * (cy1 - 2 * cy0 + y0)
# c = 3 * (cy0 - y0)
# d = y0
# cy = a*t*t*t + b*t*t + c*t + d
return [cx, cy]
end
#===============================================================================
# PictureEx
#===============================================================================
class PictureEx
attr_accessor :x # x-coordinate
attr_accessor :y # y-coordinate
attr_accessor :z # z value
attr_accessor :zoom_x # x directional zoom rate
attr_accessor :zoom_y # y directional zoom rate
attr_accessor :angle # rotation angle
attr_accessor :tone # tone
attr_accessor :color # color
attr_accessor :hue # filename hue
attr_accessor :opacity # opacity level
attr_accessor :visible # visibility boolean
attr_accessor :blend_type # blend method
attr_accessor :name # file name
attr_accessor :origin # starting point
attr_reader :src_rect # source rect
attr_reader :cropBottom # crops sprite to above this y-coordinate
attr_reader :frameUpdates # Array of processes updated in a frame
def move_processes
ret = []
@processes.each do |p|
next if ![Processes::XY, Processes::DELTA_XY].include?(p[0])
pro = []
pro.push(p[0] == Processes::XY ? "XY" : "DELTA")
if p[1] == 0 && p[2] == 0
pro.push("start " + p[7].to_i.to_s + ", " + p[8].to_i.to_s)
else
pro.push("for " + p[2].to_s) if p[2] > 0
if p[0] == Processes::XY
pro.push("go to " + p[7].to_i.to_s + ", " + p[8].to_i.to_s)
else
pro.push("move by " + p[7].to_i.to_s + ", " + p[8].to_i.to_s)
end
end
ret.push(pro)
end
return ret
end
def initialize(z)
# process: [type, delay, total_duration, frame_counter, cb, etc.]
@processes = []
@x = 0.0
@y = 0.0
@z = z
@zoom_x = 100.0
@zoom_y = 100.0
@angle = 0
@rotate_speed = 0
@auto_angle = 0 # Cumulative angle change caused by @rotate_speed
@tone = Tone.new(0, 0, 0, 0)
@tone_duration = 0
@color = Color.new(0, 0, 0, 0)
@hue = 0
@opacity = 255.0
@visible = true
@blend_type = 0
@name = ""
@origin = PictureOrigin::TOP_LEFT
@src_rect = Rect.new(0, 0, -1, -1)
@cropBottom = -1
@frameUpdates = []
end
def callback(cb)
case cb
when Proc
cb.call(self)
when Array
cb[0].method(cb[1]).call(self, *cb[2])
when Method
cb.call(self)
end
end
def setCallback(delay, cb = nil)
delay = ensureDelayAndDuration(delay)
@processes.push([nil, delay, 0, false, cb])
end
def running?
return @processes.length > 0
end
def totalDuration
ret = 0
@processes.each do |process|
dur = process[1] + process[2]
ret = dur if dur > ret
end
return ret
end
def ensureDelayAndDuration(delay, duration = nil)
delay = self.totalDuration if delay < 0
return delay, duration if !duration.nil?
return delay
end
def ensureDelay(delay)
return ensureDelayAndDuration(delay)
end
# speed is the angle to change by in 1/20 of a second. @rotate_speed is the
# angle to change by per frame.
# NOTE: This is not compatible with manually changing the angle at a certain
# point. If you make a sprite auto-rotate, you should not try to alter
# the angle another way too.
def rotate(speed)
@rotate_speed = speed * 20.0
end
def erase
self.name = ""
end
def clearProcesses
@processes = []
@timer_start = nil
end
def adjustPosition(xOffset, yOffset)
@processes.each do |process|
next if process[0] != Processes::XY
process[5] += xOffset
process[6] += yOffset
process[7] += xOffset
process[8] += yOffset
end
end
def move(delay, duration, origin, x, y, zoom_x = 100.0, zoom_y = 100.0, opacity = 255)
setOrigin(delay, duration, origin)
moveXY(delay, duration, x, y)
moveZoomXY(delay, duration, zoom_x, zoom_y)
moveOpacity(delay, duration, opacity)
end
def moveXY(delay, duration, x, y, cb = nil)
delay, duration = ensureDelayAndDuration(delay, duration)
@processes.push([Processes::XY, delay, duration, false, cb, @x, @y, x, y])
end
def setXY(delay, x, y, cb = nil)
moveXY(delay, 0, x, y, cb)
end
def moveCurve(delay, duration, x1, y1, x2, y2, x3, y3, cb = nil)
delay, duration = ensureDelayAndDuration(delay, duration)
@processes.push([Processes::CURVE, delay, duration, false, cb, [@x, @y, x1, y1, x2, y2, x3, y3]])
end
def moveDelta(delay, duration, x, y, cb = nil)
delay, duration = ensureDelayAndDuration(delay, duration)
@processes.push([Processes::DELTA_XY, delay, duration, false, cb, @x, @y, x, y])
end
def setDelta(delay, x, y, cb = nil)
moveDelta(delay, 0, x, y, cb)
end
def moveZ(delay, duration, z, cb = nil)
delay, duration = ensureDelayAndDuration(delay, duration)
@processes.push([Processes::Z, delay, duration, false, cb, @z, z])
end
def setZ(delay, z, cb = nil)
moveZ(delay, 0, z, cb)
end
def moveZoomXY(delay, duration, zoom_x, zoom_y, cb = nil)
delay, duration = ensureDelayAndDuration(delay, duration)
@processes.push([Processes::ZOOM, delay, duration, false, cb, @zoom_x, @zoom_y, zoom_x, zoom_y])
end
def setZoomXY(delay, zoom_x, zoom_y, cb = nil)
moveZoomXY(delay, 0, zoom_x, zoom_y, cb)
end
def moveZoom(delay, duration, zoom, cb = nil)
moveZoomXY(delay, duration, zoom, zoom, cb)
end
def setZoom(delay, zoom, cb = nil)
moveZoomXY(delay, 0, zoom, zoom, cb)
end
def moveAngle(delay, duration, angle, cb = nil)
delay, duration = ensureDelayAndDuration(delay, duration)
@processes.push([Processes::ANGLE, delay, duration, false, cb, @angle, angle])
end
def setAngle(delay, angle, cb = nil)
moveAngle(delay, 0, angle, cb)
end
def moveTone(delay, duration, tone, cb = nil)
delay, duration = ensureDelayAndDuration(delay, duration)
target = (tone) ? tone.clone : Tone.new(0, 0, 0, 0)
@processes.push([Processes::TONE, delay, duration, false, cb, @tone.clone, target])
end
def setTone(delay, tone, cb = nil)
moveTone(delay, 0, tone, cb)
end
def moveColor(delay, duration, color, cb = nil)
delay, duration = ensureDelayAndDuration(delay, duration)
target = (color) ? color.clone : Color.new(0, 0, 0, 0)
@processes.push([Processes::COLOR, delay, duration, false, cb, @color.clone, target])
end
def setColor(delay, color, cb = nil)
moveColor(delay, 0, color, cb)
end
# Hue changes don't actually work.
def moveHue(delay, duration, hue, cb = nil)
delay, duration = ensureDelayAndDuration(delay, duration)
@processes.push([Processes::HUE, delay, duration, false, cb, @hue, hue])
end
# Hue changes don't actually work.
def setHue(delay, hue, cb = nil)
moveHue(delay, 0, hue, cb)
end
def moveOpacity(delay, duration, opacity, cb = nil)
delay, duration = ensureDelayAndDuration(delay, duration)
@processes.push([Processes::OPACITY, delay, duration, false, cb, @opacity, opacity])
end
def setOpacity(delay, opacity, cb = nil)
moveOpacity(delay, 0, opacity, cb)
end
def setVisible(delay, visible, cb = nil)
delay = ensureDelay(delay)
@processes.push([Processes::VISIBLE, delay, 0, false, cb, visible])
end
# Only values of 0 (normal), 1 (additive) and 2 (subtractive) are allowed.
def setBlendType(delay, blend, cb = nil)
delay = ensureDelayAndDuration(delay)
@processes.push([Processes::BLEND_TYPE, delay, 0, false, cb, blend])
end
def setSE(delay, seFile, volume = nil, pitch = nil, cb = nil)
delay = ensureDelay(delay)
@processes.push([Processes::SE, delay, 0, false, cb, seFile, volume, pitch])
end
def setName(delay, name, cb = nil)
delay = ensureDelay(delay)
@processes.push([Processes::NAME, delay, 0, false, cb, name])
end
def setOrigin(delay, origin, cb = nil)
delay = ensureDelay(delay)
@processes.push([Processes::ORIGIN, delay, 0, false, cb, origin])
end
def setSrc(delay, srcX, srcY, cb = nil)
delay = ensureDelay(delay)
@processes.push([Processes::SRC, delay, 0, false, cb, srcX, srcY])
end
def setSrcSize(delay, srcWidth, srcHeight, cb = nil)
delay = ensureDelay(delay)
@processes.push([Processes::SRC_SIZE, delay, 0, false, cb, srcWidth, srcHeight])
end
# Used to cut Pokémon sprites off when they faint and sink into the ground.
def setCropBottom(delay, y, cb = nil)
delay = ensureDelay(delay)
@processes.push([Processes::CROP_BOTTOM, delay, 0, false, cb, y])
end
def update
time_now = System.uptime
@timer_start = time_now if !@timer_start
this_frame = ((time_now - @timer_start) * 20).to_i # 20 frames per second
procEnded = false
@frameUpdates.clear
@processes.each_with_index do |process, i|
# Skip processes that aren't due to start yet
next if process[1] > this_frame
# Set initial values if the process has just started
if !process[3] # Not started yet
process[3] = true # Running
case process[0]
when Processes::XY
process[5] = @x
process[6] = @y
when Processes::DELTA_XY
process[5] = @x
process[6] = @y
process[7] += @x
process[8] += @y
when Processes::CURVE
process[5][0] = @x
process[5][1] = @y
when Processes::Z
process[5] = @z
when Processes::ZOOM
process[5] = @zoom_x
process[6] = @zoom_y
when Processes::ANGLE
process[5] = @angle
when Processes::TONE
process[5] = @tone.clone
when Processes::COLOR
process[5] = @color.clone
when Processes::HUE
process[5] = @hue
when Processes::OPACITY
process[5] = @opacity
end
end
# Update process
@frameUpdates.push(process[0]) if !@frameUpdates.include?(process[0])
start_time = @timer_start + (process[1] / 20.0)
duration = process[2] / 20.0
case process[0]
when Processes::XY, Processes::DELTA_XY
@x = lerp(process[5], process[7], duration, start_time, time_now)
@y = lerp(process[6], process[8], duration, start_time, time_now)
when Processes::CURVE
@x, @y = getCubicPoint2(process[5], (time_now - start_time) / duration)
when Processes::Z
@z = lerp(process[5], process[6], duration, start_time, time_now)
when Processes::ZOOM
@zoom_x = lerp(process[5], process[7], duration, start_time, time_now)
@zoom_y = lerp(process[6], process[8], duration, start_time, time_now)
when Processes::ANGLE
@angle = lerp(process[5], process[6], duration, start_time, time_now)
when Processes::TONE
@tone.red = lerp(process[5].red, process[6].red, duration, start_time, time_now)
@tone.green = lerp(process[5].green, process[6].green, duration, start_time, time_now)
@tone.blue = lerp(process[5].blue, process[6].blue, duration, start_time, time_now)
@tone.gray = lerp(process[5].gray, process[6].gray, duration, start_time, time_now)
when Processes::COLOR
@color.red = lerp(process[5].red, process[6].red, duration, start_time, time_now)
@color.green = lerp(process[5].green, process[6].green, duration, start_time, time_now)
@color.blue = lerp(process[5].blue, process[6].blue, duration, start_time, time_now)
@color.alpha = lerp(process[5].alpha, process[6].alpha, duration, start_time, time_now)
when Processes::HUE
@hue = lerp(process[5], process[6], duration, start_time, time_now)
when Processes::OPACITY
@opacity = lerp(process[5], process[6], duration, start_time, time_now)
when Processes::VISIBLE
@visible = process[5]
when Processes::BLEND_TYPE
@blend_type = process[5]
when Processes::SE
pbSEPlay(process[5], process[6], process[7])
when Processes::NAME
@name = process[5]
when Processes::ORIGIN
@origin = process[5]
when Processes::SRC
@src_rect.x = process[5]
@src_rect.y = process[6]
when Processes::SRC_SIZE
@src_rect.width = process[5]
@src_rect.height = process[6]
when Processes::CROP_BOTTOM
@cropBottom = process[5]
end
# Erase process if its duration has elapsed
if process[1] + process[2] <= this_frame
callback(process[4]) if process[4]
@processes[i] = nil
procEnded = true
end
end
# Clear out empty spaces in @processes array caused by finished processes
@processes.compact! if procEnded
@timer_start = nil if @processes.empty? && @rotate_speed == 0
# Add the constant rotation speed
if @rotate_speed != 0
@frameUpdates.push(Processes::ANGLE) if !@frameUpdates.include?(Processes::ANGLE)
@auto_angle = @rotate_speed * (time_now - @timer_start)
while @auto_angle < 0
@auto_angle += 360
end
@auto_angle %= 360
@angle += @rotate_speed
while @angle < 0
@angle += 360
end
@angle %= 360
end
end
end
#===============================================================================
#
#===============================================================================
def setPictureSprite(sprite, picture, iconSprite = false)
return if picture.frameUpdates.length == 0
picture.frameUpdates.each do |type|
case type
when Processes::XY, Processes::DELTA_XY
sprite.x = picture.x.round
sprite.y = picture.y.round
when Processes::Z
sprite.z = picture.z
when Processes::ZOOM
sprite.zoom_x = picture.zoom_x / 100.0
sprite.zoom_y = picture.zoom_y / 100.0
when Processes::ANGLE
sprite.angle = picture.angle
when Processes::TONE
sprite.tone = picture.tone
when Processes::COLOR
sprite.color = picture.color
when Processes::HUE
# This doesn't do anything.
when Processes::BLEND_TYPE
sprite.blend_type = picture.blend_type
when Processes::OPACITY
sprite.opacity = picture.opacity
when Processes::VISIBLE
sprite.visible = picture.visible
when Processes::NAME
sprite.name = picture.name if iconSprite && sprite.name != picture.name
when Processes::ORIGIN
case picture.origin
when PictureOrigin::TOP_LEFT, PictureOrigin::LEFT, PictureOrigin::BOTTOM_LEFT
sprite.ox = 0
when PictureOrigin::TOP, PictureOrigin::CENTER, PictureOrigin::BOTTOM
sprite.ox = (sprite.bitmap && !sprite.bitmap.disposed?) ? sprite.src_rect.width / 2 : 0
when PictureOrigin::TOP_RIGHT, PictureOrigin::RIGHT, PictureOrigin::BOTTOM_RIGHT
sprite.ox = (sprite.bitmap && !sprite.bitmap.disposed?) ? sprite.src_rect.width : 0
end
case picture.origin
when PictureOrigin::TOP_LEFT, PictureOrigin::TOP, PictureOrigin::TOP_RIGHT
sprite.oy = 0
when PictureOrigin::LEFT, PictureOrigin::CENTER, PictureOrigin::RIGHT
sprite.oy = (sprite.bitmap && !sprite.bitmap.disposed?) ? sprite.src_rect.height / 2 : 0
when PictureOrigin::BOTTOM_LEFT, PictureOrigin::BOTTOM, PictureOrigin::BOTTOM_RIGHT
sprite.oy = (sprite.bitmap && !sprite.bitmap.disposed?) ? sprite.src_rect.height : 0
end
when Processes::SRC
next unless iconSprite && sprite.src_rect
sprite.src_rect.x = picture.src_rect.x
sprite.src_rect.y = picture.src_rect.y
when Processes::SRC_SIZE
next unless iconSprite && sprite.src_rect
sprite.src_rect.width = picture.src_rect.width
sprite.src_rect.height = picture.src_rect.height
end
end
if iconSprite && sprite.src_rect && picture.cropBottom >= 0
spriteBottom = sprite.y - sprite.oy + sprite.src_rect.height
if spriteBottom > picture.cropBottom
sprite.src_rect.height = [picture.cropBottom - sprite.y + sprite.oy, 0].max
end
end
end
def setPictureIconSprite(sprite, picture)
setPictureSprite(sprite, picture, true)
end

View File

@@ -1,519 +0,0 @@
class PictureOrigin
TopLeft = 0
Center = 1
TopRight = 2
BottomLeft = 3
LowerLeft = 3
BottomRight = 4
LowerRight = 4
Top = 5
Bottom = 6
Left = 7
Right = 8
end
class Processes
XY = 0
DeltaXY = 1
Z = 2
Curve = 3
Zoom = 4
Angle = 5
Tone = 6
Color = 7
Hue = 8
Opacity = 9
Visible = 10
BlendType = 11
SE = 12
Name = 13
Origin = 14
Src = 15
SrcSize = 16
CropBottom = 17
end
def getCubicPoint2(src,t)
x0 = src[0]; y0 = src[1]
cx0 = src[2]; cy0 = src[3]
cx1 = src[4]; cy1 = src[5]
x1 = src[6]; y1 = src[7]
x1 = cx1+(x1-cx1)*t
x0 = x0+(cx0-x0)*t
cx0 = cx0+(cx1-cx0)*t
cx1 = cx0+(x1-cx0)*t
cx0 = x0+(cx0-x0)*t
cx = cx0+(cx1-cx0)*t
# a = x1 - 3 * cx1 + 3 * cx0 - x0
# b = 3 * (cx1 - 2 * cx0 + x0)
# c = 3 * (cx0 - x0)
# d = x0
# cx = a*t*t*t + b*t*t + c*t + d
y1 = cy1+(y1-cy1)*t
y0 = y0+(cy0-y0)*t
cy0 = cy0+(cy1-cy0)*t
cy1 = cy0+(y1-cy0)*t
cy0 = y0+(cy0-y0)*t
cy = cy0+(cy1-cy0)*t
# a = y1 - 3 * cy1 + 3 * cy0 - y0
# b = 3 * (cy1 - 2 * cy0 + y0)
# c = 3 * (cy0 - y0)
# d = y0
# cy = a*t*t*t + b*t*t + c*t + d
return [cx,cy]
end
#===============================================================================
# PictureEx
#===============================================================================
class PictureEx
attr_accessor :x # x-coordinate
attr_accessor :y # y-coordinate
attr_accessor :z # z value
attr_accessor :zoom_x # x directional zoom rate
attr_accessor :zoom_y # y directional zoom rate
attr_accessor :angle # rotation angle
attr_accessor :tone # tone
attr_accessor :color # color
attr_accessor :hue # filename hue
attr_accessor :opacity # opacity level
attr_accessor :visible # visibility boolean
attr_accessor :blend_type # blend method
attr_accessor :name # file name
attr_accessor :origin # starting point
attr_reader :src_rect # source rect
attr_reader :cropBottom # crops sprite to above this y-coordinate
attr_reader :frameUpdates # Array of processes updated in a frame
def initialize(z)
# process: [type, delay, total_duration, frame_counter, cb, etc.]
@processes = []
@x = 0.0
@y = 0.0
@z = z
@zoom_x = 100.0
@zoom_y = 100.0
@angle = 0
@rotate_speed = 0
@tone = Tone.new(0, 0, 0, 0)
@tone_duration = 0
@color = Color.new(0, 0, 0, 0)
@hue = 0
@opacity = 255.0
@visible = true
@blend_type = 0
@name = ""
@origin = PictureOrigin::TopLeft
@src_rect = Rect.new(0,0,-1,-1)
@cropBottom = -1
@frameUpdates = []
end
def callback(cb)
if cb.is_a?(Proc); cb.call(self)
elsif cb.is_a?(Array); cb[0].method(cb[1]).call(self)
elsif cb.is_a?(Method); cb.call(self)
end
end
def setCallback(delay, cb=nil)
delay = ensureDelayAndDuration(delay)
@processes.push([nil,delay,0,0,cb])
end
def running?
return @processes.length>0
end
def totalDuration
ret = 0
for process in @processes
dur = process[1]+process[2]
ret = dur if dur>ret
end
ret *= 20.0/Graphics.frame_rate
return ret.to_i
end
def ensureDelayAndDuration(delay, duration=nil)
delay = self.totalDuration if delay<0
delay *= Graphics.frame_rate/20.0
if !duration.nil?
duration *= Graphics.frame_rate/20.0
return delay.to_i, duration.to_i
end
return delay.to_i
end
def ensureDelay(delay)
return ensureDelayAndDuration(delay)
end
# speed is the angle to change by in 1/20 of a second. @rotate_speed is the
# angle to change by per frame.
# NOTE: This is not compatible with manually changing the angle at a certain
# point. If you make a sprite auto-rotate, you should not try to alter
# the angle another way too.
def rotate(speed)
@rotate_speed = speed*20.0/Graphics.frame_rate
while @rotate_speed<0; @rotate_speed += 360; end
@rotate_speed %= 360
end
def erase
self.name = ""
end
def clearProcesses
@processes = []
end
def adjustPosition(xOffset, yOffset)
for process in @processes
next if process[0]!=Processes::XY
process[5] += xOffset
process[6] += yOffset
process[7] += xOffset
process[8] += yOffset
end
end
def move(delay, duration, origin, x, y, zoom_x=100.0, zoom_y=100.0, opacity=255)
setOrigin(delay,duration,origin)
moveXY(delay,duration,x,y)
moveZoomXY(delay,duration,zoom_x,zoom_y)
moveOpacity(delay,duration,opacity)
end
def moveXY(delay, duration, x, y, cb=nil)
delay, duration = ensureDelayAndDuration(delay,duration)
@processes.push([Processes::XY,delay,duration,0,cb,@x,@y,x,y])
end
def setXY(delay, x, y, cb=nil)
moveXY(delay,0,x,y,cb)
end
def moveCurve(delay, duration, x1, y1, x2, y2, x3, y3, cb=nil)
delay, duration = ensureDelayAndDuration(delay,duration)
@processes.push([Processes::Curve,delay,duration,0,cb,[@x,@y,x1,y1,x2,y2,x3,y3]])
end
def moveDelta(delay, duration, x, y, cb=nil)
delay, duration = ensureDelayAndDuration(delay,duration)
@processes.push([Processes::DeltaXY,delay,duration,0,cb,@x,@y,x,y])
end
def setDelta(delay, x, y, cb=nil)
moveDelta(delay,0,x,y,cb)
end
def moveZ(delay, duration, z, cb=nil)
delay, duration = ensureDelayAndDuration(delay,duration)
@processes.push([Processes::Z,delay,duration,0,cb,@z,z])
end
def setZ(delay, z, cb=nil)
moveZ(delay,0,z,cb)
end
def moveZoomXY(delay, duration, zoom_x, zoom_y, cb=nil)
delay, duration = ensureDelayAndDuration(delay,duration)
@processes.push([Processes::Zoom,delay,duration,0,cb,@zoom_x,@zoom_y,zoom_x,zoom_y])
end
def setZoomXY(delay, zoom_x, zoom_y, cb=nil)
moveZoomXY(delay,0,zoom_x,zoom_y,cb)
end
def moveZoom(delay, duration, zoom, cb=nil)
moveZoomXY(delay,duration,zoom,zoom,cb)
end
def setZoom(delay, zoom, cb=nil)
moveZoomXY(delay,0,zoom,zoom,cb)
end
def moveAngle(delay, duration, angle, cb=nil)
delay, duration = ensureDelayAndDuration(delay,duration)
@processes.push([Processes::Angle,delay,duration,0,cb,@angle,angle])
end
def setAngle(delay, angle, cb=nil)
moveAngle(delay,0,angle,cb)
end
def moveTone(delay, duration, tone, cb=nil)
delay, duration = ensureDelayAndDuration(delay,duration)
target = (tone) ? tone.clone : Tone.new(0,0,0,0)
@processes.push([Processes::Tone,delay,duration,0,cb,@tone.clone,target])
end
def setTone(delay, tone, cb=nil)
moveTone(delay,0,tone,cb)
end
def moveColor(delay, duration, color, cb=nil)
delay, duration = ensureDelayAndDuration(delay,duration)
target = (color) ? color.clone : Color.new(0,0,0,0)
@processes.push([Processes::Color,delay,duration,0,cb,@color.clone,target])
end
def setColor(delay, color, cb=nil)
moveColor(delay,0,color,cb)
end
# Hue changes don't actually work.
def moveHue(delay, duration, hue, cb=nil)
delay, duration = ensureDelayAndDuration(delay,duration)
@processes.push([Processes::Hue,delay,duration,0,cb,@hue,hue])
end
# Hue changes don't actually work.
def setHue(delay, hue, cb=nil)
moveHue(delay,0,hue,cb)
end
def moveOpacity(delay, duration, opacity, cb=nil)
delay, duration = ensureDelayAndDuration(delay,duration)
@processes.push([Processes::Opacity,delay,duration,0,cb,@opacity,opacity])
end
def setOpacity(delay, opacity, cb=nil)
moveOpacity(delay,0,opacity,cb)
end
def setVisible(delay, visible, cb=nil)
delay = ensureDelay(delay)
@processes.push([Processes::Visible,delay,0,0,cb,visible])
end
# Only values of 0 (normal), 1 (additive) and 2 (subtractive) are allowed.
def setBlendType(delay, blend, cb=nil)
delay = ensureDelayAndDuration(delay)
@processes.push([Processes::BlendType,delay,0,0,cb,blend])
end
def setSE(delay, seFile, volume=nil, pitch=nil, cb=nil)
delay = ensureDelay(delay)
@processes.push([Processes::SE,delay,0,0,cb,seFile,volume,pitch])
end
def setName(delay, name, cb=nil)
delay = ensureDelay(delay)
@processes.push([Processes::Name,delay,0,0,cb,name])
end
def setOrigin(delay, origin, cb=nil)
delay = ensureDelay(delay)
@processes.push([Processes::Origin,delay,0,0,cb,origin])
end
def setSrc(delay, srcX, srcY, cb=nil)
delay = ensureDelay(delay)
@processes.push([Processes::Src,delay,0,0,cb,srcX,srcY])
end
def setSrcSize(delay, srcWidth, srcHeight, cb=nil)
delay = ensureDelay(delay)
@processes.push([Processes::SrcSize,delay,0,0,cb,srcWidth,srcHeight])
end
# Used to cut Pokémon sprites off when they faint and sink into the ground.
def setCropBottom(delay, y, cb=nil)
delay = ensureDelay(delay)
@processes.push([Processes::CropBottom,delay,0,0,cb,y])
end
def update
procEnded = false
@frameUpdates.clear
for i in 0...@processes.length
process = @processes[i]
# Decrease delay of processes that are scheduled to start later
if process[1]>=0
# Set initial values if the process will start this frame
if process[1]==0
case process[0]
when Processes::XY
process[5] = @x
process[6] = @y
when Processes::DeltaXY
process[5] = @x
process[6] = @y
process[7] += @x
process[8] += @y
when Processes::Curve
process[5][0] = @x
process[5][1] = @y
when Processes::Z
process[5] = @z
when Processes::Zoom
process[5] = @zoom_x
process[6] = @zoom_y
when Processes::Angle
process[5] = @angle
when Processes::Tone
process[5] = @tone.clone
when Processes::Color
process[5] = @color.clone
when Processes::Hue
process[5] = @hue
when Processes::Opacity
process[5] = @opacity
end
end
# Decrease delay counter
process[1] -= 1
# Process hasn't started yet, skip to the next one
next if process[1]>=0
end
# Update process
@frameUpdates.push(process[0]) if !@frameUpdates.include?(process[0])
fra = (process[2]==0) ? 1 : process[3] # Frame counter
dur = (process[2]==0) ? 1 : process[2] # Total duration of process
case process[0]
when Processes::XY, Processes::DeltaXY
@x = process[5] + fra * (process[7] - process[5]) / dur
@y = process[6] + fra * (process[8] - process[6]) / dur
when Processes::Curve
@x, @y = getCubicPoint2(process[5],fra.to_f/dur)
when Processes::Z
@z = process[5] + fra * (process[6] - process[5]) / dur
when Processes::Zoom
@zoom_x = process[5] + fra * (process[7] - process[5]) / dur
@zoom_y = process[6] + fra * (process[8] - process[6]) / dur
when Processes::Angle
@angle = process[5] + fra * (process[6] - process[5]) / dur
when Processes::Tone
@tone.red = process[5].red + fra * (process[6].red - process[5].red) / dur
@tone.green = process[5].green + fra * (process[6].green - process[5].green) / dur
@tone.blue = process[5].blue + fra * (process[6].blue - process[5].blue) / dur
@tone.gray = process[5].gray + fra * (process[6].gray - process[5].gray) / dur
when Processes::Color
@color.red = process[5].red + fra * (process[6].red - process[5].red) / dur
@color.green = process[5].green + fra * (process[6].green - process[5].green) / dur
@color.blue = process[5].blue + fra * (process[6].blue - process[5].blue) / dur
@color.alpha = process[5].alpha + fra * (process[6].alpha - process[5].alpha) / dur
when Processes::Hue
@hue = (process[6] - process[5]).to_f / dur
when Processes::Opacity
@opacity = process[5] + fra * (process[6] - process[5]) / dur
when Processes::Visible
@visible = process[5]
when Processes::BlendType
@blend_type = process[5]
when Processes::SE
pbSEPlay(process[5],process[6],process[7])
when Processes::Name
@name = process[5]
when Processes::Origin
@origin = process[5]
when Processes::Src
@src_rect.x = process[5]
@src_rect.y = process[6]
when Processes::SrcSize
@src_rect.width = process[5]
@src_rect.height = process[6]
when Processes::CropBottom
@cropBottom = process[5]
end
# Increase frame counter
process[3] += 1
if process[3]>process[2]
# Process has ended, erase it
callback(process[4]) if process[4]
@processes[i] = nil
procEnded = true
end
end
# Clear out empty spaces in @processes array caused by finished processes
@processes.compact! if procEnded
# Add the constant rotation speed
if @rotate_speed != 0
@frameUpdates.push(Processes::Angle) if !@frameUpdates.include?(Processes::Angle)
@angle += @rotate_speed
while @angle<0; @angle += 360; end
@angle %= 360
end
end
end
#===============================================================================
#
#===============================================================================
def setPictureSprite(sprite, picture, iconSprite=false)
return if picture.frameUpdates.length==0
for i in 0...picture.frameUpdates.length
case picture.frameUpdates[i]
when Processes::XY, Processes::DeltaXY
sprite.x = picture.x.round
sprite.y = picture.y.round
when Processes::Z
sprite.z = picture.z
when Processes::Zoom
sprite.zoom_x = picture.zoom_x / 100.0
sprite.zoom_y = picture.zoom_y / 100.0
when Processes::Angle
sprite.angle = picture.angle
when Processes::Tone
sprite.tone = picture.tone
when Processes::Color
sprite.color = picture.color
when Processes::Hue
# This doesn't do anything.
when Processes::BlendType
sprite.blend_type = picture.blend_type
when Processes::Opacity
sprite.opacity = picture.opacity
when Processes::Visible
sprite.visible = picture.visible
when Processes::Name
sprite.name = picture.name if iconSprite && sprite.name != picture.name
when Processes::Origin
case picture.origin
when PictureOrigin::TopLeft, PictureOrigin::Left, PictureOrigin::BottomLeft
sprite.ox = 0
when PictureOrigin::Top, PictureOrigin::Center, PictureOrigin::Bottom
sprite.ox = (sprite.bitmap && !sprite.bitmap.disposed?) ? sprite.src_rect.width/2 : 0
when PictureOrigin::TopRight, PictureOrigin::Right, PictureOrigin::BottomRight
sprite.ox = (sprite.bitmap && !sprite.bitmap.disposed?) ? sprite.src_rect.width : 0
end
case picture.origin
when PictureOrigin::TopLeft, PictureOrigin::Top, PictureOrigin::TopRight
sprite.oy = 0
when PictureOrigin::Left, PictureOrigin::Center, PictureOrigin::Right
sprite.oy = (sprite.bitmap && !sprite.bitmap.disposed?) ? sprite.src_rect.height/2 : 0
when PictureOrigin::BottomLeft, PictureOrigin::Bottom, PictureOrigin::BottomRight
sprite.oy = (sprite.bitmap && !sprite.bitmap.disposed?) ? sprite.src_rect.height : 0
end
when Processes::Src
next unless iconSprite && sprite.src_rect
sprite.src_rect.x = picture.src_rect.x
sprite.src_rect.y = picture.src_rect.y
when Processes::SrcSize
next unless iconSprite && sprite.src_rect
sprite.src_rect.width = picture.src_rect.width
sprite.src_rect.height = picture.src_rect.height
end
end
if iconSprite && sprite.src_rect && picture.cropBottom>=0
spriteBottom = sprite.y-sprite.oy+sprite.src_rect.height
if spriteBottom>picture.cropBottom
sprite.src_rect.height = [picture.cropBottom-sprite.y+sprite.oy,0].max
end
end
end
def setPictureIconSprite(sprite, picture)
setPictureSprite(sprite,picture,true)
end

View File

@@ -0,0 +1,48 @@
#===============================================================================
#
#===============================================================================
module ScreenPosHelper
@heightcache = {}
module_function
def pbScreenZoomX(ch)
return Game_Map::TILE_WIDTH / 32.0
end
def pbScreenZoomY(ch)
return Game_Map::TILE_HEIGHT / 32.0
end
def pbScreenX(ch)
return ch.screen_x
end
def pbScreenY(ch)
return ch.screen_y
end
def bmHeight(bm)
h = @heightcache[bm]
if !h
bmap = AnimatedBitmap.new("Graphics/Characters/" + bm, 0)
h = bmap.height
@heightcache[bm] = h
bmap.dispose
end
return h
end
def pbScreenZ(ch, height = nil)
if height.nil?
height = 0
if ch.tile_id > 0
height = 32
elsif ch.character_name != ""
height = bmHeight(ch.character_name) / 4
end
end
ret = ch.screen_z(height)
return ret
end
end

View File

@@ -1,172 +0,0 @@
class Interpolator
ZOOM_X = 1
ZOOM_Y = 2
X = 3
Y = 4
OPACITY = 5
COLOR = 6
WAIT = 7
def initialize
@tweening = false
@tweensteps = []
@sprite = nil
@frames = 0
@step = 0
end
def tweening?
return @tweening
end
def tween(sprite,items,frames)
@tweensteps = []
if sprite && !sprite.disposed? && frames>0
@frames = frames
@step = 0
@sprite = sprite
for item in items
case item[0]
when ZOOM_X
@tweensteps[item[0]] = [sprite.zoom_x,item[1]-sprite.zoom_x]
when ZOOM_Y
@tweensteps[item[0]] = [sprite.zoom_y,item[1]-sprite.zoom_y]
when X
@tweensteps[item[0]] = [sprite.x,item[1]-sprite.x]
when Y
@tweensteps[item[0]] = [sprite.y,item[1]-sprite.y]
when OPACITY
@tweensteps[item[0]] = [sprite.opacity,item[1]-sprite.opacity]
when COLOR
@tweensteps[item[0]] = [sprite.color.clone,Color.new(
item[1].red-sprite.color.red,
item[1].green-sprite.color.green,
item[1].blue-sprite.color.blue,
item[1].alpha-sprite.color.alpha
)]
end
end
@tweening = true
end
end
def update
if @tweening
t = (@step*1.0)/@frames
for i in 0...@tweensteps.length
item = @tweensteps[i]
next if !item
case i
when ZOOM_X
@sprite.zoom_x = item[0]+item[1]*t
when ZOOM_Y
@sprite.zoom_y = item[0]+item[1]*t
when X
@sprite.x = item[0]+item[1]*t
when Y
@sprite.y = item[0]+item[1]*t
when OPACITY
@sprite.opacity = item[0]+item[1]*t
when COLOR
@sprite.color = Color.new(
item[0].red+item[1].red*t,
item[0].green+item[1].green*t,
item[0].blue+item[1].blue*t,
item[0].alpha+item[1].alpha*t
)
end
end
@step += 1
if @step==@frames
@step = 0
@frames = 0
@tweening = false
end
end
end
end
class RectInterpolator
def initialize(oldrect,newrect,frames)
restart(oldrect,newrect,frames)
end
def restart(oldrect,newrect,frames)
@oldrect = oldrect
@newrect = newrect
@frames = [frames,1].max
@curframe = 0
@rect = oldrect.clone
end
def set(rect)
rect.set(@rect.x,@rect.y,@rect.width,@rect.height)
end
def done?
@curframe>@frames
end
def update
return if done?
t = (@curframe*1.0/@frames)
x1 = @oldrect.x
x2 = @newrect.x
x = x1+t*(x2-x1)
y1 = @oldrect.y
y2 = @newrect.y
y = y1+t*(y2-y1)
rx1 = @oldrect.x+@oldrect.width
rx2 = @newrect.x+@newrect.width
rx = rx1+t*(rx2-rx1)
ry1 = @oldrect.y+@oldrect.height
ry2 = @newrect.y+@newrect.height
ry = ry1+t*(ry2-ry1)
minx = x<rx ? x : rx
maxx = x>rx ? x : rx
miny = y<ry ? y : ry
maxy = y>ry ? y : ry
@rect.set(minx,miny,maxx-minx,maxy-miny)
@curframe += 1
end
end
class PointInterpolator
attr_reader :x
attr_reader :y
def initialize(oldx,oldy,newx,newy,frames)
restart(oldx,oldy,newx,newy,frames)
end
def restart(oldx,oldy,newx,newy,frames)
@oldx = oldx
@oldy = oldy
@newx = newx
@newy = newy
@frames = frames
@curframe = 0
@x = oldx
@y = oldy
end
def done?
@curframe>@frames
end
def update
return if done?
t = (@curframe*1.0/@frames)
rx1 = @oldx
rx2 = @newx
@x = rx1+t*(rx2-rx1)
ry1 = @oldy
ry2 = @newy
@y = ry1+t*(ry2-ry1)
@curframe += 1
end
end

View File

@@ -1,43 +0,0 @@
module ScreenPosHelper
def self.pbScreenZoomX(ch)
return Game_Map::TILE_WIDTH/32.0
end
def self.pbScreenZoomY(ch)
return Game_Map::TILE_HEIGHT/32.0
end
def self.pbScreenX(ch)
return ch.screen_x
end
def self.pbScreenY(ch)
return ch.screen_y
end
@heightcache={}
def self.bmHeight(bm)
h=@heightcache[bm]
if !h
bmap=AnimatedBitmap.new("Graphics/Characters/"+bm,0)
h=bmap.height
@heightcache[bm]=h
bmap.dispose
end
return h
end
def self.pbScreenZ(ch,height=nil)
if height==nil
height=0
if ch.tile_id > 0
height=32
elsif ch.character_name!=""
height=bmHeight(ch.character_name)/4
end
end
ret=ch.screen_z(height)
return ret
end
end

View File

@@ -1,59 +0,0 @@
class TilemapLoader
def initialize(viewport)
@viewport = viewport
@tilemap = nil
@color = Color.new(0,0,0,0)
@tone = Tone.new(0,0,0,0)
updateClass
end
def updateClass
setClass(CustomTilemap)
end
def setClass(cls)
newtilemap = cls.new(@viewport)
if @tilemap
newtilemap.tileset = @tilemap.tileset
newtilemap.map_data = @tilemap.map_data
newtilemap.flash_data = @tilemap.flash_data
newtilemap.priorities = @tilemap.priorities
newtilemap.terrain_tags = @tilemap.terrain_tags
newtilemap.visible = @tilemap.visible
newtilemap.ox = @tilemap.ox
newtilemap.oy = @tilemap.oy
for i in 0...7
newtilemap.autotiles[i] = @tilemap.autotiles[i]
end
@tilemap.dispose
@tilemap = newtilemap
newtilemap.update
else
@tilemap = newtilemap
end
end
def dispose; @tilemap.dispose; end
def disposed?; @tilemap && @tilemap.disposed?; end
def update; @tilemap.update; end
def viewport; @tilemap.viewport; end
def autotiles; @tilemap.autotiles; end
def tileset; @tilemap.tileset; end
def tileset=(v); @tilemap.tileset = v; end
def map_data; @tilemap.map_data; end
def map_data=(v); @tilemap.map_data = v; end
def flash_data; @tilemap.flash_data; end
def flash_data=(v); @tilemap.flash_data = v; end
def priorities; @tilemap.priorities; end
def priorities=(v); @tilemap.priorities = v; end
def terrain_tags; (@tilemap.terrain_tags rescue nil); end
def terrain_tags=(v); (@tilemap.terrain_tags = v rescue nil); end
def visible; @tilemap.visible; end
def visible=(v); @tilemap.visible = v; end
def tone; (@tilemap.tone rescue @tone); end
def tone=(value); (@tilemap.tone = value rescue nil); end
def ox; @tilemap.ox; end
def ox=(v); @tilemap.ox = v; end
def oy; @tilemap.oy; end
def oy=(v); @tilemap.oy = v; end
end

View File

@@ -0,0 +1,643 @@
#===============================================================================
#
#===============================================================================
class TilemapRenderer
attr_reader :tilesets
attr_reader :autotiles
attr_accessor :tone
attr_accessor :color
attr_reader :viewport
attr_accessor :ox # Does nothing
attr_accessor :oy # Does nothing
attr_accessor :visible # Does nothing
DISPLAY_TILE_WIDTH = Game_Map::TILE_WIDTH rescue 32
DISPLAY_TILE_HEIGHT = Game_Map::TILE_HEIGHT rescue 32
SOURCE_TILE_WIDTH = 32
SOURCE_TILE_HEIGHT = 32
ZOOM_X = DISPLAY_TILE_WIDTH / SOURCE_TILE_WIDTH
ZOOM_Y = DISPLAY_TILE_HEIGHT / SOURCE_TILE_HEIGHT
TILESET_TILES_PER_ROW = 8
AUTOTILES_COUNT = 8 # Counting the blank tile as an autotile
TILES_PER_AUTOTILE = 48
TILESET_START_ID = AUTOTILES_COUNT * TILES_PER_AUTOTILE
# If an autotile's filename ends with "[x]", its frame duration will be x/20
# seconds instead.
AUTOTILE_FRAME_DURATION = 5 # In 1/20ths of a second
# Filenames of extra autotiles for each tileset. Each tileset's entry is an
# array containing two other arrays (you can leave either of those empty, but
# they must be defined):
# - The first sub-array is for large autotiles, i.e. ones with 48 different
# tile layouts. For example, "Brick path" and "Sea".
# - The second is for single tile autotiles. For example, "Flowers1" and
# "Waterfall"
# The top tiles of the tileset will instead use these autotiles. Large
# autotiles come first, in the same 8x6 layout as you see when you double-
# click on a real autotile in RMXP. After that are the single tile autotiles.
# Extra autotiles are only useful if the tiles are animated, because otherwise
# you just have some tiles which belong in the tileset instead.
EXTRA_AUTOTILES = {
# Examples:
# 1 => [["Sand shore"], ["Flowers2"]],
# 2 => [[], ["Flowers2", "Waterfall", "Waterfall crest", "Waterfall bottom"]],
# 6 => [["Water rock", "Sea deep"], []]
}
#=============================================================================
#
#=============================================================================
class TilesetBitmaps
attr_accessor :changed
attr_accessor :bitmaps
def initialize
@bitmaps = {}
@bitmap_wraps = {} # Whether each tileset is a mega texture and has multiple columns
@load_counts = {}
@bridge = 0
@changed = true
end
def [](filename)
return @bitmaps[filename]
end
def []=(filename, bitmap)
return if nil_or_empty?(filename)
@bitmaps[filename] = bitmap
@bitmap_wraps[filename] = false
@changed = true
end
def add(filename)
return if nil_or_empty?(filename)
if @bitmaps[filename]
@load_counts[filename] += 1
return
end
bitmap = pbGetTileset(filename)
@bitmap_wraps[filename] = false
if bitmap.mega?
self[filename] = TilemapRenderer::TilesetWrapper.wrapTileset(bitmap)
@bitmap_wraps[filename] = true
bitmap.dispose
else
self[filename] = bitmap
end
@load_counts[filename] = 1
end
def remove(filename)
return if nil_or_empty?(filename) || !@bitmaps[filename]
if @load_counts[filename] > 1
@load_counts[filename] -= 1
return
end
@bitmaps[filename].dispose
@bitmaps.delete(filename)
@bitmap_wraps.delete(filename)
@load_counts.delete(filename)
end
def set_src_rect(tile, tile_id)
return if nil_or_empty?(tile.filename)
return if !@bitmaps[tile.filename]
tile.src_rect.x = ((tile_id - TILESET_START_ID) % TILESET_TILES_PER_ROW) * SOURCE_TILE_WIDTH
tile.src_rect.y = ((tile_id - TILESET_START_ID) / TILESET_TILES_PER_ROW) * SOURCE_TILE_HEIGHT
if @bitmap_wraps[tile.filename]
height = @bitmaps[tile.filename].height
col = (tile_id - TILESET_START_ID) * SOURCE_TILE_HEIGHT / (TILESET_TILES_PER_ROW * height)
tile.src_rect.x += col * TILESET_TILES_PER_ROW * SOURCE_TILE_WIDTH
tile.src_rect.y -= col * height
end
end
def update; end
end
#=============================================================================
#
#=============================================================================
class AutotileBitmaps < TilesetBitmaps
attr_reader :current_frames
def initialize
super
@frame_counts = {} # Number of frames in each autotile
@frame_durations = {} # How long each frame lasts per autotile
@current_frames = {} # Which frame each autotile is currently showing
@timer_start = System.uptime
end
def []=(filename, value)
super
return if nil_or_empty?(filename)
frame_count(filename, true)
set_current_frame(filename)
end
EXPANDED_AUTOTILES_FOLDER = "Graphics/Autotiles/ExpandedAutotiles/"
def add(filename)
return if nil_or_empty?(filename)
if @bitmaps[filename]
@load_counts[filename] += 1
return
end
# Try to load expanded autotile from cache first
cached_path = File.join("Graphics", "Autotiles/ExpandedAutotiles", "#{filename}.png")
if FileTest.exist?(cached_path)
bitmap = RPG::Cache.load_bitmap(EXPANDED_AUTOTILES_FOLDER, filename)
duration = AUTOTILE_FRAME_DURATION
if filename[/\[\s*(\d+?)\s*\]\s*$/]
duration = $~[1].to_i
end
@frame_durations[filename] = duration.to_f / 20
else
orig_bitmap = pbGetAutotile(filename)
@bitmap_wraps[filename] = false
duration = AUTOTILE_FRAME_DURATION
if filename[/\[\s*(\d+?)\s*\]\s*$/]
duration = $~[1].to_i
end
@frame_durations[filename] = duration.to_f / 20
expanded_bitmap = AutotileExpander.expand(orig_bitmap)
# Save expanded bitmap to cache for next time
Dir.mkdir(EXPANDED_AUTOTILES_FOLDER) unless Dir.exist?(EXPANDED_AUTOTILES_FOLDER)
expanded_bitmap.save_to_png(cached_path)
bitmap = expanded_bitmap
orig_bitmap.dispose if orig_bitmap != expanded_bitmap
end
self[filename] = bitmap
if bitmap.height > SOURCE_TILE_HEIGHT && bitmap.height < TILES_PER_AUTOTILE * SOURCE_TILE_HEIGHT
@bitmap_wraps[filename] = true
end
@load_counts[filename] = 1
end
def remove(filename)
super
return if @load_counts[filename] && @load_counts[filename] > 0
@frame_counts.delete(filename)
@current_frames.delete(filename)
@frame_durations.delete(filename)
end
def frame_count(filename, force_recalc = false)
if !@frame_counts[filename] || force_recalc
return 0 if !@bitmaps[filename]
bitmap = @bitmaps[filename]
@frame_counts[filename] = [bitmap.width / SOURCE_TILE_WIDTH, 1].max
if bitmap.height > SOURCE_TILE_HEIGHT && @bitmap_wraps[filename]
@frame_counts[filename] /= 2
end
end
return @frame_counts[filename]
end
def animated?(filename)
return frame_count(filename) > 1
end
def current_frame(filename)
set_current_frame(filename) if !@current_frames[filename]
return @current_frames[filename]
end
def set_current_frame(filename)
frames = frame_count(filename)
if frames < 2
@current_frames[filename] = 0
else
@current_frames[filename] = ((System.uptime - @timer_start) / @frame_durations[filename]).floor % frames
end
end
def set_src_rect(tile, tile_id)
return if nil_or_empty?(tile.filename)
return if !@bitmaps[tile.filename]
frame = current_frame(tile.filename)
if @bitmaps[tile.filename].height == SOURCE_TILE_HEIGHT
tile.src_rect.x = frame * SOURCE_TILE_WIDTH
tile.src_rect.y = 0
return
end
wraps = @bitmap_wraps[tile.filename]
high_id = ((tile_id % TILES_PER_AUTOTILE) >= TILES_PER_AUTOTILE / 2)
tile.src_rect.x = 0
tile.src_rect.y = (tile_id % TILES_PER_AUTOTILE) * SOURCE_TILE_HEIGHT
if wraps && high_id
tile.src_rect.x = SOURCE_TILE_WIDTH
tile.src_rect.y -= SOURCE_TILE_HEIGHT * TILES_PER_AUTOTILE / 2
end
tile.src_rect.x += frame * SOURCE_TILE_WIDTH * (wraps ? 2 : 1)
end
def update
super
# Update the current frame for each autotile
@bitmaps.each_key do |filename|
next if !@bitmaps[filename] || @bitmaps[filename].disposed?
old_frame = @current_frames[filename]
set_current_frame(filename)
@changed = true if @current_frames[filename] != old_frame
end
end
end
#=============================================================================
#
#=============================================================================
class TileSprite < Sprite
attr_accessor :filename
attr_accessor :tile_id
attr_accessor :is_autotile
attr_accessor :animated
attr_accessor :priority
attr_accessor :shows_reflection
attr_accessor :bridge
attr_accessor :need_refresh
def set_bitmap(filename, tile_id, autotile, animated, priority, bitmap)
self.bitmap = bitmap
self.src_rect = Rect.new(0, 0, SOURCE_TILE_WIDTH, SOURCE_TILE_HEIGHT)
self.zoom_x = ZOOM_X
self.zoom_y = ZOOM_Y
@filename = filename
@tile_id = tile_id
@is_autotile = autotile
@animated = animated
@priority = priority
@shows_reflection = false
@bridge = false
self.visible = !bitmap.nil?
@need_refresh = true
end
end
#-----------------------------------------------------------------------------
def initialize(viewport)
@tilesets = TilesetBitmaps.new
@autotiles = AutotileBitmaps.new
@tiles_horizontal_count = (Graphics.width.to_f / DISPLAY_TILE_WIDTH).ceil + 1
@tiles_vertical_count = (Graphics.height.to_f / DISPLAY_TILE_HEIGHT).ceil + 1
@tone = Tone.new(0, 0, 0, 0)
@old_tone = Tone.new(0, 0, 0, 0)
@color = Color.new(0, 0, 0, 0)
@old_color = Color.new(0, 0, 0, 0)
@self_viewport = Viewport.new(0, 0, Graphics.width, Graphics.height)
@viewport = (viewport) ? viewport : @self_viewport
@old_viewport_ox = 0
@old_viewport_oy = 0
# NOTE: The extra tiles horizontally/vertically hang off the left and top
# edges of the screen, because the pixel_offset values are positive
# and are added to the tile sprite coordinates.
@tiles = []
@tiles_horizontal_count.times do |i|
@tiles[i] = []
@tiles_vertical_count.times do |j|
@tiles[i][j] = Array.new(3) { TileSprite.new(@viewport) }
end
end
@current_map_id = 0
@tile_offset_x = 0
@tile_offset_y = 0
@pixel_offset_x = 0
@pixel_offset_y = 0
@ox = 0
@oy = 0
@visible = true
@need_refresh = true
@disposed = false
end
def dispose
return if disposed?
@tiles.each do |col|
col.each do |coord|
coord.each { |tile| tile.dispose }
coord.clear
end
end
@tiles.clear
@tilesets.bitmaps.each_value { |bitmap| bitmap.dispose }
@tilesets.bitmaps.clear
@autotiles.bitmaps.each_value { |bitmap| bitmap.dispose }
@autotiles.bitmaps.clear
@self_viewport.dispose
@self_viewport = nil
@disposed = true
end
def disposed?
return @disposed
end
#-----------------------------------------------------------------------------
def add_tileset(filename)
@tilesets.add(filename)
end
def remove_tileset(filename)
@tilesets.remove(filename)
end
def add_autotile(filename)
@autotiles.add(filename)
end
def remove_autotile(filename)
@autotiles.remove(filename)
end
def add_extra_autotiles(tileset_id)
return if !EXTRA_AUTOTILES[tileset_id]
EXTRA_AUTOTILES[tileset_id].each do |arr|
arr.each { |filename| add_autotile(filename) }
end
end
def remove_extra_autotiles(tileset_id)
return if !EXTRA_AUTOTILES[tileset_id]
EXTRA_AUTOTILES[tileset_id].each do |arr|
arr.each { |filename| remove_autotile(filename) }
end
end
#-----------------------------------------------------------------------------
def refresh
@need_refresh = true
end
def refresh_tile_bitmap(tile, map, tile_id)
tile.tile_id = tile_id
if tile_id < TILES_PER_AUTOTILE
tile.set_bitmap("", tile_id, false, false, 0, nil)
tile.shows_reflection = false
tile.bridge = false
else
terrain_tag = map.terrain_tags[tile_id] || 0
terrain_tag_data = GameData::TerrainTag.try_get(terrain_tag)
priority = map.priorities[tile_id] || 0
single_autotile_start_id = TILESET_START_ID
true_tileset_start_id = TILESET_START_ID
extra_autotile_arrays = EXTRA_AUTOTILES[map.tileset_id]
if extra_autotile_arrays
large_autotile_count = extra_autotile_arrays[0].length
single_autotile_count = extra_autotile_arrays[1].length
single_autotile_start_id += large_autotile_count * TILES_PER_AUTOTILE
true_tileset_start_id += large_autotile_count * TILES_PER_AUTOTILE
true_tileset_start_id += single_autotile_count
end
if tile_id < true_tileset_start_id
filename = ""
if tile_id < TILESET_START_ID # Real autotiles
filename = map.autotile_names[(tile_id / TILES_PER_AUTOTILE) - 1]
elsif tile_id < single_autotile_start_id # Large extra autotiles
filename = extra_autotile_arrays[0][(tile_id - TILESET_START_ID) / TILES_PER_AUTOTILE]
else
# Single extra autotiles
filename = extra_autotile_arrays[1][tile_id - single_autotile_start_id]
end
tile.set_bitmap(filename, tile_id, true, @autotiles.animated?(filename),
priority, @autotiles[filename])
else
filename = map.tileset_name
tile.set_bitmap(filename, tile_id, false, false, priority, @tilesets[filename])
end
tile.shows_reflection = terrain_tag_data&.shows_reflections
tile.bridge = terrain_tag_data&.bridge
end
refresh_tile_src_rect(tile, tile_id)
end
def refresh_tile_src_rect(tile, tile_id)
if tile.is_autotile
@autotiles.set_src_rect(tile, tile_id)
else
@tilesets.set_src_rect(tile, tile_id)
end
end
# For animated autotiles only
def refresh_tile_frame(tile, tile_id)
return if !tile.animated
@autotiles.set_src_rect(tile, tile_id)
end
# x and y are the positions of tile within @tiles, not a map x/y
def refresh_tile_coordinates(tile, x, y)
tile.x = (x * DISPLAY_TILE_WIDTH) - @pixel_offset_x
tile.y = (y * DISPLAY_TILE_HEIGHT) - @pixel_offset_y
end
def refresh_tile_z(tile, map, y, layer, tile_id)
if tile.shows_reflection
tile.z = -2000
elsif tile.bridge && $PokemonGlobal.bridge > 0
tile.z = 0
else
priority = tile.priority
tile.z = (priority == 0) ? 0 : (y * SOURCE_TILE_HEIGHT) + (priority * SOURCE_TILE_HEIGHT) + SOURCE_TILE_HEIGHT + 1
end
end
def refresh_tile(tile, x, y, map, layer, tile_id)
refresh_tile_bitmap(tile, map, tile_id)
refresh_tile_coordinates(tile, x, y)
refresh_tile_z(tile, map, y, layer, tile_id)
tile.need_refresh = false
end
#-----------------------------------------------------------------------------
def check_if_screen_moved
ret = false
# Check for map change
if @current_map_id != $game_map.map_id
if MapFactoryHelper.hasConnections?(@current_map_id)
offsets = $map_factory.getRelativePos(@current_map_id, 0, 0, $game_map.map_id, 0, 0)
if offsets
@tile_offset_x -= offsets[0]
@tile_offset_y -= offsets[1]
else
ret = true # Need a full refresh
end
else
ret = true
end
@current_map_id = $game_map.map_id
end
# Check for tile movement
current_map_display_x = ($game_map.display_x.to_f / Game_Map::X_SUBPIXELS).round
current_map_display_y = ($game_map.display_y.to_f / Game_Map::Y_SUBPIXELS).round
new_tile_offset_x = (current_map_display_x / SOURCE_TILE_WIDTH) * ZOOM_X
new_tile_offset_y = (current_map_display_y / SOURCE_TILE_HEIGHT) * ZOOM_Y
if new_tile_offset_x != @tile_offset_x
if new_tile_offset_x > @tile_offset_x
# Take tile stacks off the right and insert them at the beginning (left)
(new_tile_offset_x - @tile_offset_x).times do
c = @tiles.shift
@tiles.push(c)
c.each do |coord|
coord.each { |tile| tile.need_refresh = true }
end
end
else
# Take tile stacks off the beginning (left) and push them onto the end (right)
(@tile_offset_x - new_tile_offset_x).times do
c = @tiles.pop
@tiles.prepend(c)
c.each do |coord|
coord.each { |tile| tile.need_refresh = true }
end
end
end
@screen_moved = true
@tile_offset_x = new_tile_offset_x
end
if new_tile_offset_y != @tile_offset_y
if new_tile_offset_y > @tile_offset_y
# Take tile stacks off the bottom and insert them at the beginning (top)
@tiles.each do |col|
(new_tile_offset_y - @tile_offset_y).times do
c = col.shift
col.push(c)
c.each { |tile| tile.need_refresh = true }
end
end
else
# Take tile stacks off the beginning (top) and push them onto the end (bottom)
@tiles.each do |col|
(@tile_offset_y - new_tile_offset_y).times do
c = col.pop
col.prepend(c)
c.each { |tile| tile.need_refresh = true }
end
end
end
@screen_moved = true
@screen_moved_vertically = true
@tile_offset_y = new_tile_offset_y
end
# Check for pixel movement
new_pixel_offset_x = (current_map_display_x % SOURCE_TILE_WIDTH) * ZOOM_X
new_pixel_offset_y = (current_map_display_y % SOURCE_TILE_HEIGHT) * ZOOM_Y
if new_pixel_offset_x != @pixel_offset_x
@screen_moved = true
@pixel_offset_x = new_pixel_offset_x
end
if new_pixel_offset_y != @pixel_offset_y
@screen_moved = true
@screen_moved_vertically = true
@pixel_offset_y = new_pixel_offset_y
end
return ret
end
#-----------------------------------------------------------------------------
def update
# Update tone
if @old_tone != @tone
@tiles.each do |col|
col.each do |coord|
coord.each { |tile| tile.tone = @tone }
end
end
@old_tone = @tone.clone
end
# Update color
if @old_color != @color
@tiles.each do |col|
col.each do |coord|
coord.each { |tile| tile.color = @color }
end
end
@old_color = @color.clone
end
# Recalculate autotile frames
@tilesets.update
@autotiles.update
do_full_refresh = @need_refresh
if @viewport.ox != @old_viewport_ox || @viewport.oy != @old_viewport_oy
@old_viewport_ox = @viewport.ox
@old_viewport_oy = @viewport.oy
do_full_refresh = true
end
# Check whether the screen has moved since the last update
@screen_moved = false
@screen_moved_vertically = false
if $PokemonGlobal.bridge != @bridge
@bridge = $PokemonGlobal.bridge
@screen_moved_vertically = true # To update bridge tiles' z values
end
do_full_refresh = true if check_if_screen_moved
# Update all tile sprites
visited = []
@tiles_horizontal_count.times do |i|
visited[i] = []
@tiles_vertical_count.times { |j| visited[i][j] = false }
end
$map_factory.maps.each do |map|
# Calculate x/y ranges of tile sprites that represent them
map_display_x = (map.display_x.to_f / Game_Map::X_SUBPIXELS).round
map_display_x = ((map_display_x + (Graphics.width / 2)) * ZOOM_X) - (Graphics.width / 2) if ZOOM_X != 1
map_display_y = (map.display_y.to_f / Game_Map::Y_SUBPIXELS).round
map_display_y = ((map_display_y + (Graphics.height / 2)) * ZOOM_Y) - (Graphics.height / 2) if ZOOM_Y != 1
map_display_x_tile = map_display_x / DISPLAY_TILE_WIDTH
map_display_y_tile = map_display_y / DISPLAY_TILE_HEIGHT
start_x = [-map_display_x_tile, 0].max
start_y = [-map_display_y_tile, 0].max
end_x = @tiles_horizontal_count - 1
end_x = [end_x, map.width - map_display_x_tile - 1].min
end_y = @tiles_vertical_count - 1
end_y = [end_y, map.height - map_display_y_tile - 1].min
next if start_x > end_x || start_y > end_y || end_x < 0 || end_y < 0
# Update all tile sprites representing this map
(start_x..end_x).each do |i|
tile_x = i + map_display_x_tile
(start_y..end_y).each do |j|
tile_y = j + map_display_y_tile
@tiles[i][j].each_with_index do |tile, layer|
tile_id = map.data[tile_x, tile_y, layer]
if do_full_refresh || tile.need_refresh || tile.tile_id != tile_id
refresh_tile(tile, i, j, map, layer, tile_id)
else
refresh_tile_frame(tile, tile_id) if tile.animated && @autotiles.changed
# Update tile's x/y coordinates
refresh_tile_coordinates(tile, i, j) if @screen_moved
# Update tile's z value
refresh_tile_z(tile, map, j, layer, tile_id) if @screen_moved_vertically
end
end
# Record x/y as visited
visited[i][j] = true
end
end
end
# Clear all unvisited tile sprites
@tiles.each_with_index do |col, i|
col.each_with_index do |coord, j|
next if visited[i][j]
coord.each do |tile|
tile.set_bitmap("", 0, false, false, 0, nil)
tile.shows_reflection = false
tile.bridge = false
end
end
end
@need_refresh = false
@autotiles.changed = false
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,97 @@
#===============================================================================
# This module is a little fix that works around PC hardware limitations. Since
# Essentials isn't working with software rendering anymore, it now has to deal
# with the limits of the GPU. For the most part this is no big deal, but people
# do have some really big tilesets.
#
# The fix is simple enough: If your tileset is too big, a new bitmap will be
# constructed with all the excess pixels sent to the image's right side. This
# basically means that you now have a limit far higher than you should ever
# actually need.
#
# Hardware limit -> max tileset length:
# 1024px -> 4096px
# 2048px -> 16384px (enough to get the normal limit)
# 4096px -> 65536px (enough to load pretty much any tileset)
# 8192px -> 262144px
# 16384px -> 1048576px (what most people have at this point)
#===============================================================================
class TilemapRenderer
module TilesetWrapper
TILESET_WIDTH = SOURCE_TILE_WIDTH * TILESET_TILES_PER_ROW
# Looks useless, but covers weird numbers given to mkxp.json or a funky driver
MAX_TEX_SIZE = (Bitmap.max_size / 1024) * 1024
MAX_TEX_SIZE_BOOSTED = (MAX_TEX_SIZE**2) / TILESET_WIDTH
module_function
def wrapTileset(originalbmp)
width = originalbmp.width
height = originalbmp.height
if width == TILESET_WIDTH && originalbmp.mega?
columns = (height / MAX_TEX_SIZE.to_f).ceil
if columns * TILESET_WIDTH > MAX_TEX_SIZE
raise "Tileset is too long!\n\nSIZE: #{originalbmp.height}px\nHARDWARE LIMIT: #{MAX_TEX_SIZE}px\nBOOSTED LIMIT: #{MAX_TEX_SIZE_BOOSTED}px"
end
bmp = Bitmap.new(TILESET_WIDTH * columns, MAX_TEX_SIZE)
remainder = height % MAX_TEX_SIZE
remainder = MAX_TEX_SIZE if remainder == 0
columns.times do |col|
srcrect = Rect.new(0, col * MAX_TEX_SIZE, width, (col + 1 == columns) ? remainder : MAX_TEX_SIZE)
bmp.blt(col * TILESET_WIDTH, 0, originalbmp, srcrect)
end
return bmp
end
return originalbmp
end
def getWrappedRect(src_rect)
ret = Rect.new(0, 0, 0, 0)
col = (src_rect.y / MAX_TEX_SIZE.to_f).floor
ret.x = (col * TILESET_WIDTH) + src_rect.x.clamp(0, TILESET_WIDTH)
ret.y = src_rect.y % MAX_TEX_SIZE
ret.width = src_rect.width.clamp(0, TILESET_WIDTH - src_rect.x)
ret.height = src_rect.height.clamp(0, MAX_TEX_SIZE)
return ret
end
#---------------------------------------------------------------------------
private
def blitWrappedPixels(destX, destY, dest, src, srcrect)
if srcrect.y + srcrect.width < MAX_TEX_SIZE
# Save the processing power
dest.blt(destX, destY, src, srcrect)
return
end
merge = (srcrect.y % MAX_TEX_SIZE) > ((srcrect.y + srcrect.height) % MAX_TEX_SIZE)
srcrect_mod = getWrappedRect(srcrect)
if merge
# FIXME: won't work on heights longer than two columns, but nobody should need
# more than 32k pixels high at once anyway
side = {
:a => MAX_TEX_SIZE - srcrect_mod.y,
:b => srcrect_mod.height - MAX_TEX_SIZE + srcrect_mod.y
}
dest.blt(destX, destY, src, Rect.new(srcrect_mod.x, srcrect_mod.y, srcrect_mod.width, side[:a]))
dest.blt(destX, destY + side[:a], src, Rect.new(srcrect_mod.x + TILESET_WIDTH, 0, srcrect_mod.width, side[:b]))
else
dest.blt(destX, destY, src, srcrect_mod)
end
end
def stretchBlitWrappedPixels(destrect, dest, src, srcrect)
if srcrect.y + srcrect.width < MAX_TEX_SIZE
# Save the processing power
dest.stretch_blt(destrect, src, srcrect)
return
end
# Does a regular blit to a non-megasurface, then stretch_blts that to
# the destination. Yes it is slow
tmp = Bitmap.new(srcrect.width, srcrect.height)
blitWrappedPixels(0, 0, tmp, src, srcrect)
dest.stretch_blt(destrect, tmp, Rect.new(0, 0, srcrect.width, srcrect.height))
end
end
end

View File

@@ -0,0 +1,75 @@
#===============================================================================
#
#===============================================================================
class TilemapRenderer
module AutotileExpander
MAX_TEXTURE_SIZE = (Bitmap.max_size / 1024) * 1024
module_function
# This doesn't allow for cache sizes smaller than 768, but if that applies
# to you, you've got bigger problems.
def expand(bitmap)
return bitmap if bitmap.height == SOURCE_TILE_HEIGHT
expanded_format = (bitmap.height == SOURCE_TILE_HEIGHT * 6)
wrap = false
if MAX_TEXTURE_SIZE < TILES_PER_AUTOTILE * SOURCE_TILE_HEIGHT
wrap = true # Each autotile will occupy two columns instead of one
end
frames_count = [bitmap.width / (3 * SOURCE_TILE_WIDTH), 1].max
new_bitmap = Bitmap.new(frames_count * (wrap ? 2 : 1) * SOURCE_TILE_WIDTH,
TILES_PER_AUTOTILE * SOURCE_TILE_HEIGHT / (wrap ? 2 : 1))
rect = Rect.new(0, 0, SOURCE_TILE_WIDTH / 2, SOURCE_TILE_HEIGHT / 2)
TILES_PER_AUTOTILE.times do |id|
pattern = TileDrawingHelper::AUTOTILE_PATTERNS[id >> 3][id % TILESET_TILES_PER_ROW]
wrap_offset_x = (wrap && id >= TILES_PER_AUTOTILE / 2) ? SOURCE_TILE_WIDTH : 0
wrap_offset_y = (wrap && id >= TILES_PER_AUTOTILE / 2) ? (TILES_PER_AUTOTILE / 2) * SOURCE_TILE_HEIGHT : 0
frames_count.times do |frame|
if expanded_format && [1, 2, 4, 8].include?(id)
dest_x = frame * SOURCE_TILE_WIDTH * (wrap ? 2 : 1)
dest_x += wrap_offset_x
next if dest_x > MAX_TEXTURE_SIZE
dest_y = id * SOURCE_TILE_HEIGHT
dest_y -= wrap_offset_y
next if dest_y > MAX_TEXTURE_SIZE
case id
when 1 # Top left corner
new_bitmap.blt(dest_x, dest_y, bitmap,
Rect.new(frame * SOURCE_TILE_WIDTH * 3, SOURCE_TILE_HEIGHT * 4,
SOURCE_TILE_WIDTH, SOURCE_TILE_HEIGHT))
when 2 # Top right corner
new_bitmap.blt(dest_x, dest_y, bitmap,
Rect.new(SOURCE_TILE_WIDTH + (frame * SOURCE_TILE_WIDTH * 3), SOURCE_TILE_HEIGHT * 4,
SOURCE_TILE_WIDTH, SOURCE_TILE_HEIGHT))
when 4 # Bottom right corner
new_bitmap.blt(dest_x, dest_y, bitmap,
Rect.new(SOURCE_TILE_WIDTH + (frame * SOURCE_TILE_WIDTH * 3), SOURCE_TILE_HEIGHT * 5,
SOURCE_TILE_WIDTH, SOURCE_TILE_HEIGHT))
when 8 # Bottom left corner
new_bitmap.blt(dest_x, dest_y, bitmap,
Rect.new(frame * SOURCE_TILE_WIDTH * 3, SOURCE_TILE_HEIGHT * 5,
SOURCE_TILE_WIDTH, SOURCE_TILE_HEIGHT))
end
next
end
pattern.each_with_index do |src_chunk, i|
real_src_chunk = src_chunk - 1
dest_x = (i % 2) * SOURCE_TILE_WIDTH / 2
dest_x += frame * SOURCE_TILE_WIDTH * (wrap ? 2 : 1)
dest_x += wrap_offset_x
next if dest_x > MAX_TEXTURE_SIZE
dest_y = (i / 2) * SOURCE_TILE_HEIGHT / 2
dest_y += id * SOURCE_TILE_HEIGHT
dest_y -= wrap_offset_y
next if dest_y > MAX_TEXTURE_SIZE
rect.x = (real_src_chunk % 6) * SOURCE_TILE_WIDTH / 2
rect.x += SOURCE_TILE_WIDTH * 3 * frame
rect.y = (real_src_chunk / 6) * SOURCE_TILE_HEIGHT / 2
new_bitmap.blt(dest_x, dest_y, bitmap, rect)
end
end
end
return new_bitmap
end
end
end

View File

@@ -1,228 +0,0 @@
class TileDrawingHelper
attr_accessor :tileset
attr_accessor :autotiles
Autotiles = [
[ [27, 28, 33, 34], [ 5, 28, 33, 34], [27, 6, 33, 34], [ 5, 6, 33, 34],
[27, 28, 33, 12], [ 5, 28, 33, 12], [27, 6, 33, 12], [ 5, 6, 33, 12] ],
[ [27, 28, 11, 34], [ 5, 28, 11, 34], [27, 6, 11, 34], [ 5, 6, 11, 34],
[27, 28, 11, 12], [ 5, 28, 11, 12], [27, 6, 11, 12], [ 5, 6, 11, 12] ],
[ [25, 26, 31, 32], [25, 6, 31, 32], [25, 26, 31, 12], [25, 6, 31, 12],
[15, 16, 21, 22], [15, 16, 21, 12], [15, 16, 11, 22], [15, 16, 11, 12] ],
[ [29, 30, 35, 36], [29, 30, 11, 36], [ 5, 30, 35, 36], [ 5, 30, 11, 36],
[39, 40, 45, 46], [ 5, 40, 45, 46], [39, 6, 45, 46], [ 5, 6, 45, 46] ],
[ [25, 30, 31, 36], [15, 16, 45, 46], [13, 14, 19, 20], [13, 14, 19, 12],
[17, 18, 23, 24], [17, 18, 11, 24], [41, 42, 47, 48], [ 5, 42, 47, 48] ],
[ [37, 38, 43, 44], [37, 6, 43, 44], [13, 18, 19, 24], [13, 14, 43, 44],
[37, 42, 43, 48], [17, 18, 47, 48], [13, 18, 43, 48], [ 1, 2, 7, 8] ]
]
# converts neighbors returned from tableNeighbors to tile indexes
NeighborsToTiles = [
46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40,
42, 32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16,
46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40,
42, 32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16,
45, 39, 45, 39, 33, 31, 33, 29, 45, 39, 45, 39, 33, 31, 33, 29,
37, 27, 37, 27, 23, 15, 23, 13, 37, 27, 37, 27, 22, 11, 22, 9,
45, 39, 45, 39, 33, 31, 33, 29, 45, 39, 45, 39, 33, 31, 33, 29,
36, 26, 36, 26, 21, 7, 21, 5, 36, 26, 36, 26, 20, 3, 20, 1,
46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40,
42, 32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16,
46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40,
42, 32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16,
45, 38, 45, 38, 33, 30, 33, 28, 45, 38, 45, 38, 33, 30, 33, 28,
37, 25, 37, 25, 23, 14, 23, 12, 37, 25, 37, 25, 22, 10, 22, 8,
45, 38, 45, 38, 33, 30, 33, 28, 45, 38, 45, 38, 33, 30, 33, 28,
36, 24, 36, 24, 21, 6, 21, 4, 36, 24, 36, 24, 20, 2, 20, 0
]
def self.tableNeighbors(data,x,y)
return 0 if x < 0 || x >= data.xsize
return 0 if y < 0 || y >= data.ysize
t = data[x,y]
xp1 = [x + 1, data.xsize - 1].min
yp1 = [y + 1, data.ysize - 1].min
xm1 = [x - 1, 0].max
ym1 = [y - 1, 0].max
i = 0
i |= 0x01 if data[ x, ym1] == t # N
i |= 0x02 if data[xp1, ym1] == t # NE
i |= 0x04 if data[xp1, y] == t # E
i |= 0x08 if data[xp1, yp1] == t # SE
i |= 0x10 if data[ x, yp1] == t # S
i |= 0x20 if data[xm1, yp1] == t # SW
i |= 0x40 if data[xm1, y] == t # W
i |= 0x80 if data[xm1, ym1] == t # NW
return i
end
def self.fromTileset(tileset)
bmtileset=pbGetTileset(tileset.tileset_name)
bmautotiles=[]
for i in 0...7
bmautotiles.push(pbGetAutotile(tileset.autotile_names[i]))
end
return self.new(bmtileset,bmautotiles)
end
def initialize(tileset, autotiles)
if tileset.mega?
@tileset = TileWrap::wrapTileset(tileset)
tileset.dispose
@shouldWrap = true
else
@tileset = tileset
@shouldWrap = false
end
@autotiles = autotiles
end
def dispose
@tileset.dispose if @tileset
@tileset = nil
for i in 0...@autotiles.length
@autotiles[i].dispose
@autotiles[i] = nil
end
end
def bltSmallAutotile(bitmap,x,y,cxTile,cyTile,id,frame)
return if id >= 384 || frame < 0 || !@autotiles
autotile = @autotiles[id / 48 - 1]
return if !autotile || autotile.disposed?
cxTile = [cxTile / 2, 1].max
cyTile = [cyTile / 2, 1].max
if autotile.height == 32
anim = frame * 32
src_rect = Rect.new(anim, 0, 32, 32)
bitmap.stretch_blt(Rect.new(x, y, cxTile * 2, cyTile * 2), autotile, src_rect)
else
anim = frame * 96
id %= 48
tiles = TileDrawingHelper::Autotiles[id >> 3][id & 7]
src = Rect.new(0, 0, 0, 0)
for i in 0...4
tile_position = tiles[i] - 1
src.set(tile_position % 6 * 16 + anim, tile_position / 6 * 16, 16, 16)
bitmap.stretch_blt(Rect.new(i % 2 * cxTile + x, i / 2 * cyTile + y, cxTile, cyTile),
autotile, src)
end
end
end
def bltSmallRegularTile(bitmap,x,y,cxTile,cyTile,id)
return if id < 384 || !@tileset || @tileset.disposed?
rect = Rect.new((id - 384) % 8 * 32, (id - 384) / 8 * 32, 32, 32)
rect = TileWrap::getWrappedRect(rect) if @shouldWrap
bitmap.stretch_blt(Rect.new(x, y, cxTile, cyTile), @tileset, rect)
end
def bltSmallTile(bitmap,x,y,cxTile,cyTile,id,frame=0)
if id >= 384
bltSmallRegularTile(bitmap, x, y, cxTile, cyTile, id)
elsif id > 0
bltSmallAutotile(bitmap, x, y, cxTile, cyTile, id, frame)
end
end
def bltAutotile(bitmap,x,y,id,frame)
bltSmallAutotile(bitmap, x, y, 32, 32, id, frame)
end
def bltRegularTile(bitmap,x,y,id)
bltSmallRegularTile(bitmap, x, y, 32, 32, id)
end
def bltTile(bitmap,x,y,id,frame=0)
if id >= 384
bltRegularTile(bitmap, x, y, id)
elsif id > 0
bltAutotile(bitmap, x, y, id, frame)
end
end
end
#===============================================================================
#
#===============================================================================
def createMinimap(mapid)
map=load_data(sprintf("Data/Map%03d.rxdata",mapid)) rescue nil
return BitmapWrapper.new(32,32) if !map
bitmap=BitmapWrapper.new(map.width*4,map.height*4)
black=Color.new(0,0,0)
tilesets=$data_tilesets
tileset=tilesets[map.tileset_id]
return bitmap if !tileset
helper=TileDrawingHelper.fromTileset(tileset)
for y in 0...map.height
for x in 0...map.width
for z in 0..2
id=map.data[x,y,z]
id=0 if !id
helper.bltSmallTile(bitmap,x*4,y*4,4,4,id)
end
end
end
bitmap.fill_rect(0,0,bitmap.width,1,black)
bitmap.fill_rect(0,bitmap.height-1,bitmap.width,1,black)
bitmap.fill_rect(0,0,1,bitmap.height,black)
bitmap.fill_rect(bitmap.width-1,0,1,bitmap.height,black)
return bitmap
end
def bltMinimapAutotile(dstBitmap,x,y,srcBitmap,id)
return if id>=48 || !srcBitmap || srcBitmap.disposed?
anim=0
cxTile=3
cyTile=3
tiles = TileDrawingHelper::Autotiles[id>>3][id&7]
src=Rect.new(0,0,0,0)
for i in 0...4
tile_position = tiles[i] - 1
src.set(
tile_position % 6 * cxTile + anim,
tile_position / 6 * cyTile, cxTile, cyTile)
dstBitmap.blt(i%2*cxTile+x,i/2*cyTile+y, srcBitmap, src)
end
end
def passable?(passages,tile_id)
return false if tile_id == nil
passage = passages[tile_id]
return (passage && passage<15)
end
# Unused
def getPassabilityMinimap(mapid)
map = load_data(sprintf("Data/Map%03d.rxdata",mapid))
tileset = $data_tilesets[map.tileset_id]
minimap = AnimatedBitmap.new("Graphics/Pictures/minimap_tiles")
ret = Bitmap.new(map.width*6,map.height*6)
passtable = Table.new(map.width,map.height)
passages = tileset.passages
for i in 0...map.width
for j in 0...map.height
pass=true
for z in [2,1,0]
if !passable?(passages,map.data[i,j,z])
pass=false
break
end
end
passtable[i,j]=pass ? 1 : 0
end
end
neighbors=TileDrawingHelper::NeighborsToTiles
for i in 0...map.width
for j in 0...map.height
if passtable[i,j]==0
nb=TileDrawingHelper.tableNeighbors(passtable,i,j)
tile=neighbors[nb]
bltMinimapAutotile(ret,i*6,j*6,minimap.bitmap,tile)
end
end
end
minimap.disposes
return ret
end

View File

@@ -0,0 +1,246 @@
#===============================================================================
#
#===============================================================================
class TileDrawingHelper
attr_accessor :tileset
attr_accessor :autotiles
AUTOTILE_PATTERNS = [
[[27, 28, 33, 34], [5, 28, 33, 34], [27, 6, 33, 34], [5, 6, 33, 34],
[27, 28, 33, 12], [5, 28, 33, 12], [27, 6, 33, 12], [5, 6, 33, 12]],
[[27, 28, 11, 34], [5, 28, 11, 34], [27, 6, 11, 34], [5, 6, 11, 34],
[27, 28, 11, 12], [5, 28, 11, 12], [27, 6, 11, 12], [5, 6, 11, 12]],
[[25, 26, 31, 32], [25, 6, 31, 32], [25, 26, 31, 12], [25, 6, 31, 12],
[15, 16, 21, 22], [15, 16, 21, 12], [15, 16, 11, 22], [15, 16, 11, 12]],
[[29, 30, 35, 36], [29, 30, 11, 36], [5, 30, 35, 36], [5, 30, 11, 36],
[39, 40, 45, 46], [5, 40, 45, 46], [39, 6, 45, 46], [5, 6, 45, 46]],
[[25, 30, 31, 36], [15, 16, 45, 46], [13, 14, 19, 20], [13, 14, 19, 12],
[17, 18, 23, 24], [17, 18, 11, 24], [41, 42, 47, 48], [5, 42, 47, 48]],
[[37, 38, 43, 44], [37, 6, 43, 44], [13, 18, 19, 24], [13, 14, 43, 44],
[37, 42, 43, 48], [17, 18, 47, 48], [13, 18, 43, 48], [1, 2, 7, 8]]
]
# converts neighbors returned from tableNeighbors to tile indexes
NEIGHBORS_TO_AUTOTILE_INDEX = [
46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40,
42, 32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16,
46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40,
42, 32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16,
45, 39, 45, 39, 33, 31, 33, 29, 45, 39, 45, 39, 33, 31, 33, 29,
37, 27, 37, 27, 23, 15, 23, 13, 37, 27, 37, 27, 22, 11, 22, 9,
45, 39, 45, 39, 33, 31, 33, 29, 45, 39, 45, 39, 33, 31, 33, 29,
36, 26, 36, 26, 21, 7, 21, 5, 36, 26, 36, 26, 20, 3, 20, 1,
46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40,
42, 32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16,
46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40,
42, 32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16,
45, 38, 45, 38, 33, 30, 33, 28, 45, 38, 45, 38, 33, 30, 33, 28,
37, 25, 37, 25, 23, 14, 23, 12, 37, 25, 37, 25, 22, 10, 22, 8,
45, 38, 45, 38, 33, 30, 33, 28, 45, 38, 45, 38, 33, 30, 33, 28,
36, 24, 36, 24, 21, 6, 21, 4, 36, 24, 36, 24, 20, 2, 20, 0
]
def self.tableNeighbors(data, x, y, layer = nil)
return 0 if x < 0 || x >= data.xsize
return 0 if y < 0 || y >= data.ysize
if layer.nil?
t = data[x, y]
else
t = data[x, y, layer]
end
xp1 = [x + 1, data.xsize - 1].min
yp1 = [y + 1, data.ysize - 1].min
xm1 = [x - 1, 0].max
ym1 = [y - 1, 0].max
i = 0
if layer.nil?
i |= 0x01 if data[ x, ym1] == t # N
i |= 0x02 if data[xp1, ym1] == t # NE
i |= 0x04 if data[xp1, y] == t # E
i |= 0x08 if data[xp1, yp1] == t # SE
i |= 0x10 if data[ x, yp1] == t # S
i |= 0x20 if data[xm1, yp1] == t # SW
i |= 0x40 if data[xm1, y] == t # W
i |= 0x80 if data[xm1, ym1] == t # NW
else
i |= 0x01 if data[ x, ym1, layer] == t # N
i |= 0x02 if data[xp1, ym1, layer] == t # NE
i |= 0x04 if data[xp1, y, layer] == t # E
i |= 0x08 if data[xp1, yp1, layer] == t # SE
i |= 0x10 if data[ x, yp1, layer] == t # S
i |= 0x20 if data[xm1, yp1, layer] == t # SW
i |= 0x40 if data[xm1, y, layer] == t # W
i |= 0x80 if data[xm1, ym1, layer] == t # NW
end
return i
end
def self.fromTileset(tileset)
bmtileset = pbGetTileset(tileset.tileset_name)
bmautotiles = []
7.times do |i|
bmautotiles.push(pbGetAutotile(tileset.autotile_names[i]))
end
return self.new(bmtileset, bmautotiles)
end
#-----------------------------------------------------------------------------
def initialize(tileset, autotiles)
if tileset.mega?
@tileset = TilemapRenderer::TilesetWrapper.wrapTileset(tileset)
tileset.dispose
@shouldWrap = true
else
@tileset = tileset
@shouldWrap = false
end
@autotiles = autotiles
end
def dispose
@tileset&.dispose
@tileset = nil
@autotiles.each_with_index do |autotile, i|
autotile.dispose
@autotiles[i] = nil
end
end
def bltSmallAutotile(bitmap, x, y, cxTile, cyTile, id, frame)
return if id >= 384 || frame < 0 || !@autotiles
autotile = @autotiles[(id / 48) - 1]
return if !autotile || autotile.disposed?
cxTile = [cxTile / 2, 1].max
cyTile = [cyTile / 2, 1].max
if autotile.height == 32
anim = frame * 32
src_rect = Rect.new(anim, 0, 32, 32)
bitmap.stretch_blt(Rect.new(x, y, cxTile * 2, cyTile * 2), autotile, src_rect)
else
anim = frame * 96
id %= 48
tiles = AUTOTILE_PATTERNS[id >> 3][id & 7]
src = Rect.new(0, 0, 0, 0)
4.times do |i|
tile_position = tiles[i] - 1
src.set(((tile_position % 6) * 16) + anim, (tile_position / 6) * 16, 16, 16)
bitmap.stretch_blt(Rect.new((i % 2 * cxTile) + x, (i / 2 * cyTile) + y, cxTile, cyTile),
autotile, src)
end
end
end
def bltSmallRegularTile(bitmap, x, y, cxTile, cyTile, id)
return if id < 384 || !@tileset || @tileset.disposed?
rect = Rect.new(((id - 384) % 8) * 32, ((id - 384) / 8) * 32, 32, 32)
rect = TilemapRenderer::TilesetWrapper.getWrappedRect(rect) if @shouldWrap
bitmap.stretch_blt(Rect.new(x, y, cxTile, cyTile), @tileset, rect)
end
def bltSmallTile(bitmap, x, y, cxTile, cyTile, id, frame = 0)
if id >= 384
bltSmallRegularTile(bitmap, x, y, cxTile, cyTile, id)
elsif id > 0
bltSmallAutotile(bitmap, x, y, cxTile, cyTile, id, frame)
end
end
def bltAutotile(bitmap, x, y, id, frame)
bltSmallAutotile(bitmap, x, y, 32, 32, id, frame)
end
def bltRegularTile(bitmap, x, y, id)
bltSmallRegularTile(bitmap, x, y, 32, 32, id)
end
def bltTile(bitmap, x, y, id, frame = 0)
if id >= 384
bltRegularTile(bitmap, x, y, id)
elsif id > 0
bltAutotile(bitmap, x, y, id, frame)
end
end
end
#===============================================================================
#
#===============================================================================
def createMinimap(mapid)
map = load_data(sprintf("Data/Map%03d.rxdata", mapid)) rescue nil
return Bitmap.new(32, 32) if !map
bitmap = Bitmap.new(map.width * 4, map.height * 4)
black = Color.black
tilesets = $data_tilesets
tileset = tilesets[map.tileset_id]
return bitmap if !tileset
helper = TileDrawingHelper.fromTileset(tileset)
map.height.times do |y|
map.width.times do |x|
3.times do |z|
id = map.data[x, y, z]
id = 0 if !id
helper.bltSmallTile(bitmap, x * 4, y * 4, 4, 4, id)
end
end
end
bitmap.fill_rect(0, 0, bitmap.width, 1, black)
bitmap.fill_rect(0, bitmap.height - 1, bitmap.width, 1, black)
bitmap.fill_rect(0, 0, 1, bitmap.height, black)
bitmap.fill_rect(bitmap.width - 1, 0, 1, bitmap.height, black)
return bitmap
end
def bltMinimapAutotile(dstBitmap, x, y, srcBitmap, id)
return if id >= 48 || !srcBitmap || srcBitmap.disposed?
anim = 0
cxTile = 3
cyTile = 3
tiles = TileDrawingHelper::AUTOTILE_PATTERNS[id >> 3][id & 7]
src = Rect.new(0, 0, 0, 0)
4.times do |i|
tile_position = tiles[i] - 1
src.set((tile_position % 6 * cxTile) + anim,
tile_position / 6 * cyTile, cxTile, cyTile)
dstBitmap.blt((i % 2 * cxTile) + x, (i / 2 * cyTile) + y, srcBitmap, src)
end
end
def passable?(passages, tile_id)
return false if tile_id.nil?
passage = passages[tile_id]
return (passage && passage < 15)
end
# Unused
def getPassabilityMinimap(mapid)
map = load_data(sprintf("Data/Map%03d.rxdata", mapid))
tileset = $data_tilesets[map.tileset_id]
minimap = AnimatedBitmap.new("Graphics/UI/minimap_tiles")
ret = Bitmap.new(map.width * 6, map.height * 6)
passtable = Table.new(map.width, map.height)
passages = tileset.passages
map.width.times do |i|
map.height.times do |j|
pass = true
[2, 1, 0].each do |z|
if !passable?(passages, map.data[i, j, z])
pass = false
break
end
end
passtable[i, j] = pass ? 1 : 0
end
end
neighbors = TileDrawingHelper::NEIGHBORS_TO_AUTOTILE_INDEX
map.width.times do |i|
map.height.times do |j|
next if passtable[i, j] != 0
nb = TileDrawingHelper.tableNeighbors(passtable, i, j)
tile = neighbors[nb]
bltMinimapAutotile(ret, i * 6, j * 6, minimap.bitmap, tile)
end
end
minimap.dispose
return ret
end

View File

@@ -1,13 +1,17 @@
#===============================================================================
#
#===============================================================================
class Hangup < Exception; end
#===============================================================================
#
#===============================================================================
module RPG
module Cache
def self.debug
t = Time.now
filename = t.strftime("%H %M %S.%L.txt")
File.open("cache_" + filename, "wb") { |f|
File.open("cache_" + filename, "wb") do |f|
@cache.each do |key, value|
if !value
f.write("#{key} (nil)\r\n")
@@ -17,7 +21,7 @@ module RPG
f.write("#{key} (#{value.refcount}, #{value.width}x#{value.height})\r\n")
end
end
}
end
end
def self.setKey(key, obj)
@@ -27,7 +31,7 @@ module RPG
def self.fromCache(i)
return nil if !@cache.include?(i)
obj = @cache[i]
return nil if obj && obj.disposed?
return nil if obj&.disposed?
return obj
end
@@ -67,7 +71,7 @@ module RPG
ret.addRef
else
ret = BitmapWrapper.new(32 * width, 32 * height)
x = (tile_id - 384) % 8 * 32
x = ((tile_id - 384) % 8) * 32
y = (((tile_id - 384) / 8) - height + 1) * 32
tileset = yield(filename)
ret.blt(0, 0, tileset, Rect.new(x, y, 32 * width, 32 * height))
@@ -86,6 +90,10 @@ module RPG
self.load_bitmap("Graphics/Transitions/", filename)
end
def self.ui(filename)
self.load_bitmap("Graphics/UI/", filename)
end
def self.retain(folder_name, filename = "", hue = 0)
path = folder_name + filename
ret = fromCache(path)
@@ -102,8 +110,9 @@ module RPG
end
end
#===============================================================================
#
#===============================================================================
class BitmapWrapper < Bitmap
attr_reader :refcount
attr_accessor :never_dispose

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,6 @@
#===============================================================================
#
#===============================================================================
class WindowCursorRect < Rect
def initialize(window)
super(0, 0, 0, 0)
@@ -6,7 +9,6 @@ class WindowCursorRect < Rect
def empty
return unless needs_update?(0, 0, 0, 0)
set(0, 0, 0, 0)
end
@@ -16,9 +18,7 @@ class WindowCursorRect < Rect
def set(x, y, width, height)
return unless needs_update?(x, y, width, height)
super(x, y, width, height)
@window.width = @window.width
end
@@ -42,6 +42,8 @@ class WindowCursorRect < Rect
@window.width = @window.width
end
#-----------------------------------------------------------------------------
private
def needs_update?(x, y, width, height)
@@ -49,7 +51,9 @@ class WindowCursorRect < Rect
end
end
#===============================================================================
#
#===============================================================================
class Window
attr_reader :tone
attr_reader :color
@@ -78,108 +82,107 @@ class Window
@_windowskin
end
def initialize(viewport=nil)
@sprites={}
@spritekeys=[
"back",
"corner0","side0","scroll0",
"corner1","side1","scroll1",
"corner2","side2","scroll2",
"corner3","side3","scroll3",
"cursor","contents","pause"
def initialize(viewport = nil)
@sprites = {}
@spritekeys = [
"back",
"corner0", "side0", "scroll0",
"corner1", "side1", "scroll1",
"corner2", "side2", "scroll2",
"corner3", "side3", "scroll3",
"cursor", "contents", "pause"
]
@sidebitmaps=[nil,nil,nil,nil]
@cursorbitmap=nil
@bgbitmap=nil
@viewport=viewport
for i in @spritekeys
@sprites[i]=Sprite.new(@viewport)
@sidebitmaps = [nil, nil, nil, nil]
@cursorbitmap = nil
@bgbitmap = nil
@viewport = viewport
@spritekeys.each do |i|
@sprites[i] = Sprite.new(@viewport)
end
@disposed=false
@tone=Tone.new(0,0,0)
@color=Color.new(0,0,0,0)
@blankcontents=Bitmap.new(1,1) # RGSS2 requires this
@contents=@blankcontents
@_windowskin=nil
@rpgvx=false # Set to true to emulate RPGVX windows
@x=0
@y=0
@width=0
@openness=255
@height=0
@ox=0
@oy=0
@z=0
@stretch=true
@visible=true
@active=true
@blend_type=0
@contents_blend_type=0
@opacity=255
@back_opacity=255
@contents_opacity=255
@cursor_rect=WindowCursorRect.new(self)
@cursorblink=0
@cursoropacity=255
@pause=false
@pauseopacity=255
@pauseframe=0
@disposed = false
@tone = Tone.new(0, 0, 0)
@color = Color.new(0, 0, 0, 0)
@blankcontents = Bitmap.new(1, 1) # RGSS2 requires this
@contents = @blankcontents
@_windowskin = nil
@rpgvx = false # Set to true to emulate RPGVX windows
@x = 0
@y = 0
@width = 0
@openness = 255
@height = 0
@ox = 0
@oy = 0
@z = 0
@stretch = true
@visible = true
@active = true
@blend_type = 0
@contents_blend_type = 0
@opacity = 255
@back_opacity = 255
@contents_opacity = 255
@cursor_rect = WindowCursorRect.new(self)
@cursoropacity = 255
@pause = false
@pauseopacity = 255
@pauseframe = 0
privRefresh(true)
end
def dispose
if !self.disposed?
for i in @sprites
i[1].dispose if i[1]
@sprites[i[0]]=nil
@sprites.each do |i|
i[1]&.dispose
@sprites[i[0]] = nil
end
for i in 0...@sidebitmaps.length
@sidebitmaps[i].dispose if @sidebitmaps[i]
@sidebitmaps[i]=nil
@sidebitmaps.each_with_index do |bitmap, i|
bitmap&.dispose
@sidebitmaps[i] = nil
end
@blankcontents.dispose
@cursorbitmap.dispose if @cursorbitmap
@backbitmap.dispose if @backbitmap
@cursorbitmap&.dispose
@backbitmap&.dispose
@sprites.clear
@sidebitmaps.clear
@_windowskin=nil
@_contents=nil
@disposed=true
@_windowskin = nil
@_contents = nil
@disposed = true
end
end
def openness=(value)
@openness=value
@openness=0 if @openness<0
@openness=255 if @openness>255
@openness = value
@openness = 0 if @openness < 0
@openness = 255 if @openness > 255
privRefresh
end
def stretch=(value)
@stretch=value
@stretch = value
privRefresh(true)
end
def visible=(value)
@visible=value
@visible = value
privRefresh
end
def viewport=(value)
@viewport=value
for i in @spritekeys
@viewport = value
@spritekeys.each do |i|
@sprites[i].dispose
if @sprites[i].is_a?(Sprite)
@sprites[i]=Sprite.new(@viewport)
@sprites[i] = Sprite.new(@viewport)
else
@sprites[i]=nil
@sprites[i] = nil
end
end
privRefresh(true)
end
def z=(value)
@z=value
@z = value
privRefresh
end
@@ -188,417 +191,415 @@ class Window
end
def contents=(value)
@contents=value
@contents = value
privRefresh
end
def windowskin=(value)
@_windowskin=value
if value && value.is_a?(Bitmap) && !value.disposed? && value.width==128
@rpgvx=true
@_windowskin = value
if value.is_a?(Bitmap) && !value.disposed? && value.width == 128
@rpgvx = true
else
@rpgvx=false
@rpgvx = false
end
privRefresh(true)
end
def ox=(value)
@ox=value
@ox = value
privRefresh
end
def active=(value)
@active=value
@active = value
privRefresh(true)
end
def cursor_rect=(value)
if !value
@cursor_rect.empty
if value
@cursor_rect.set(value.x, value.y, value.width, value.height)
else
@cursor_rect.set(value.x,value.y,value.width,value.height)
@cursor_rect.empty
end
end
def oy=(value)
@oy=value
@oy = value
privRefresh
end
def width=(value)
@width=value
@width = value
privRefresh(true)
end
def height=(value)
@height=value
@height = value
privRefresh(true)
end
def pause=(value)
@pause=value
@pauseopacity=0 if !value
@pause = value
@pauseopacity = 0 if !value
privRefresh
end
def x=(value)
@x=value
@x = value
privRefresh
end
def y=(value)
@y=value
@y = value
privRefresh
end
def opacity=(value)
@opacity=value
@opacity=0 if @opacity<0
@opacity=255 if @opacity>255
@opacity = value
@opacity = 0 if @opacity < 0
@opacity = 255 if @opacity > 255
privRefresh
end
def back_opacity=(value)
@back_opacity=value
@back_opacity=0 if @back_opacity<0
@back_opacity=255 if @back_opacity>255
@back_opacity = value
@back_opacity = 0 if @back_opacity < 0
@back_opacity = 255 if @back_opacity > 255
privRefresh
end
def contents_opacity=(value)
@contents_opacity=value
@contents_opacity=0 if @contents_opacity<0
@contents_opacity=255 if @contents_opacity>255
@contents_opacity = value
@contents_opacity = 0 if @contents_opacity < 0
@contents_opacity = 255 if @contents_opacity > 255
privRefresh
end
def tone=(value)
@tone=value
@tone = value
privRefresh
end
def color=(value)
@color=value
@color = value
privRefresh
end
def blend_type=(value)
@blend_type=value
@blend_type = value
privRefresh
end
def flash(color,duration)
def flash(color, duration)
return if disposed?
for i in @sprites
i[1].flash(color,duration)
@sprites.each do |i|
i[1].flash(color, duration)
end
end
def update
return if disposed?
mustchange=false
mustchange = false
if @active
if @cursorblink==0
@cursoropacity-=8
@cursorblink=1 if @cursoropacity<=128
cursor_time = System.uptime / 0.4
if cursor_time.to_i.even?
@cursoropacity = lerp(255, 128, 0.4, cursor_time % 2)
else
@cursoropacity+=8
@cursorblink=0 if @cursoropacity>=255
@cursoropacity = lerp(128, 255, 0.4, (cursor_time - 1) % 2)
end
mustchange=true if !@cursor_rect.empty?
mustchange = true if !@cursor_rect.empty?
else
mustchange=true if @cursoropacity!=128
@cursoropacity=128
mustchange = true if @cursoropacity != 128
@cursoropacity = 128
end
if @pause
@pauseframe=(Graphics.frame_count / 8) % 4
@pauseopacity=[@pauseopacity+64,255].min
mustchange=true
@pauseframe = (System.uptime * 5).to_i % 4 # 4 frames, 5 frames per second
@pauseopacity = [@pauseopacity + 64, 255].min
mustchange = true
end
privRefresh if mustchange
for i in @sprites
@sprites.each do |i|
i[1].update
end
end
#-----------------------------------------------------------------------------
private
def ensureBitmap(bitmap,dwidth,dheight)
if !bitmap||bitmap.disposed?||bitmap.width<dwidth||bitmap.height<dheight
bitmap.dispose if bitmap
bitmap=Bitmap.new([1,dwidth].max,[1,dheight].max)
def ensureBitmap(bitmap, dwidth, dheight)
if !bitmap || bitmap.disposed? || bitmap.width < dwidth || bitmap.height < dheight
bitmap&.dispose
bitmap = Bitmap.new([1, dwidth].max, [1, dheight].max)
end
return bitmap
end
def tileBitmap(dstbitmap,dstrect,srcbitmap,srcrect)
def tileBitmap(dstbitmap, dstrect, srcbitmap, srcrect)
return if !srcbitmap || srcbitmap.disposed?
left=dstrect.x
top=dstrect.y
y=0;loop do break unless y<dstrect.height
x=0;loop do break unless x<dstrect.width
dstbitmap.blt(x+left,y+top,srcbitmap,srcrect)
x+=srcrect.width
left = dstrect.x
top = dstrect.y
y = 0
loop do
break unless y < dstrect.height
x = 0
loop do
break unless x < dstrect.width
dstbitmap.blt(x + left, y + top, srcbitmap, srcrect)
x += srcrect.width
end
y+=srcrect.height
y += srcrect.height
end
end
def privRefresh(changeBitmap=false)
def privRefresh(changeBitmap = false)
return if self.disposed?
backopac=self.back_opacity*self.opacity/255
contopac=self.contents_opacity
cursoropac=@cursoropacity*contopac/255
for i in 0...4
@sprites["corner#{i}"].bitmap=@_windowskin
@sprites["scroll#{i}"].bitmap=@_windowskin
backopac = self.back_opacity * self.opacity / 255
contopac = self.contents_opacity
cursoropac = @cursoropacity * contopac / 255
4.times do |i|
@sprites["corner#{i}"].bitmap = @_windowskin
@sprites["scroll#{i}"].bitmap = @_windowskin
end
@sprites["pause"].bitmap=@_windowskin
@sprites["contents"].bitmap=@contents
@sprites["pause"].bitmap = @_windowskin
@sprites["contents"].bitmap = @contents
if @_windowskin && !@_windowskin.disposed?
for i in 0...4
@sprites["corner#{i}"].opacity=@opacity
@sprites["corner#{i}"].tone=@tone
@sprites["corner#{i}"].color=@color
@sprites["corner#{i}"].blend_type=@blend_type
@sprites["corner#{i}"].visible=@visible
@sprites["side#{i}"].opacity=@opacity
@sprites["side#{i}"].tone=@tone
@sprites["side#{i}"].color=@color
@sprites["side#{i}"].blend_type=@blend_type
@sprites["side#{i}"].visible=@visible
@sprites["scroll#{i}"].opacity=@opacity
@sprites["scroll#{i}"].tone=@tone
@sprites["scroll#{i}"].blend_type=@blend_type
@sprites["scroll#{i}"].color=@color
@sprites["scroll#{i}"].visible=@visible
4.times do |i|
@sprites["corner#{i}"].opacity = @opacity
@sprites["corner#{i}"].tone = @tone
@sprites["corner#{i}"].color = @color
@sprites["corner#{i}"].blend_type = @blend_type
@sprites["corner#{i}"].visible = @visible
@sprites["side#{i}"].opacity = @opacity
@sprites["side#{i}"].tone = @tone
@sprites["side#{i}"].color = @color
@sprites["side#{i}"].blend_type = @blend_type
@sprites["side#{i}"].visible = @visible
@sprites["scroll#{i}"].opacity = @opacity
@sprites["scroll#{i}"].tone = @tone
@sprites["scroll#{i}"].blend_type = @blend_type
@sprites["scroll#{i}"].color = @color
@sprites["scroll#{i}"].visible = @visible
end
for i in ["back","cursor","pause","contents"]
@sprites[i].color=@color
@sprites[i].tone=@tone
@sprites[i].blend_type=@blend_type
["back", "cursor", "pause", "contents"].each do |i|
@sprites[i].color = @color
@sprites[i].tone = @tone
@sprites[i].blend_type = @blend_type
end
@sprites["contents"].blend_type=@contents_blend_type
@sprites["back"].opacity=backopac
@sprites["contents"].opacity=contopac
@sprites["cursor"].opacity=cursoropac
@sprites["pause"].opacity=@pauseopacity
@sprites["back"].visible=@visible
@sprites["contents"].visible=@visible && @openness==255
@sprites["pause"].visible=@visible && @pause
@sprites["cursor"].visible=@visible && @openness==255
hascontents=(@contents && !@contents.disposed?)
@sprites["contents"].blend_type = @contents_blend_type
@sprites["back"].opacity = backopac
@sprites["contents"].opacity = contopac
@sprites["cursor"].opacity = cursoropac
@sprites["pause"].opacity = @pauseopacity
@sprites["back"].visible = @visible
@sprites["contents"].visible = @visible && @openness == 255
@sprites["pause"].visible = @visible && @pause
@sprites["cursor"].visible = @visible && @openness == 255
hascontents = (@contents && !@contents.disposed?)
@sprites["scroll0"].visible = @visible && hascontents && @oy > 0
@sprites["scroll1"].visible = @visible && hascontents && @ox > 0
@sprites["scroll2"].visible = @visible && hascontents &&
(@contents.width - @ox) > @width-32
(@contents.width - @ox) > @width - 32
@sprites["scroll3"].visible = @visible && hascontents &&
(@contents.height - @oy) > @height-32
(@contents.height - @oy) > @height - 32
else
for i in 0...4
@sprites["corner#{i}"].visible=false
@sprites["side#{i}"].visible=false
@sprites["scroll#{i}"].visible=false
4.times do |i|
@sprites["corner#{i}"].visible = false
@sprites["side#{i}"].visible = false
@sprites["scroll#{i}"].visible = false
end
@sprites["contents"].visible=@visible && @openness==255
@sprites["contents"].color=@color
@sprites["contents"].tone=@tone
@sprites["contents"].blend_type=@contents_blend_type
@sprites["contents"].opacity=contopac
@sprites["back"].visible=false
@sprites["pause"].visible=false
@sprites["cursor"].visible=false
@sprites["contents"].visible = @visible && @openness == 255
@sprites["contents"].color = @color
@sprites["contents"].tone = @tone
@sprites["contents"].blend_type = @contents_blend_type
@sprites["contents"].opacity = contopac
@sprites["back"].visible = false
@sprites["pause"].visible = false
@sprites["cursor"].visible = false
end
for i in @sprites
i[1].z=@z
@sprites.each do |i|
i[1].z = @z
end
if @rpgvx
@sprites["cursor"].z=@z # For Compatibility
@sprites["contents"].z=@z # For Compatibility
@sprites["pause"].z=@z # For Compatibility
@sprites["cursor"].z = @z # For Compatibility
@sprites["contents"].z = @z # For Compatibility
@sprites["pause"].z = @z # For Compatibility
else
@sprites["cursor"].z=@z+1 # For Compatibility
@sprites["contents"].z=@z+2 # For Compatibility
@sprites["pause"].z=@z+2 # For Compatibility
@sprites["cursor"].z = @z + 1 # For Compatibility
@sprites["contents"].z = @z + 2 # For Compatibility
@sprites["pause"].z = @z + 2 # For Compatibility
end
if @rpgvx
trimX=64
trimY=0
backRect=Rect.new(0,0,64,64)
blindsRect=Rect.new(0,64,64,64)
trimX = 64
trimY = 0
backRect = Rect.new(0, 0, 64, 64)
blindsRect = Rect.new(0, 64, 64, 64)
else
trimX=128
trimY=0
backRect=Rect.new(0,0,128,128)
blindsRect=nil
trimX = 128
trimY = 0
backRect = Rect.new(0, 0, 128, 128)
blindsRect = nil
end
@sprites["corner0"].src_rect.set(trimX,trimY+0,16,16);
@sprites["corner1"].src_rect.set(trimX+48,trimY+0,16,16);
@sprites["corner2"].src_rect.set(trimX,trimY+48,16,16);
@sprites["corner3"].src_rect.set(trimX+48,trimY+48,16,16);
@sprites["scroll0"].src_rect.set(trimX+24, trimY+16, 16, 8) # up
@sprites["scroll3"].src_rect.set(trimX+24, trimY+40, 16, 8) # down
@sprites["scroll1"].src_rect.set(trimX+16, trimY+24, 8, 16) # left
@sprites["scroll2"].src_rect.set(trimX+40, trimY+24, 8, 16) # right
cursorX=trimX
cursorY=trimY+64
sideRects=[
Rect.new(trimX+16,trimY+0,32,16),
Rect.new(trimX,trimY+16,16,32),
Rect.new(trimX+48,trimY+16,16,32),
Rect.new(trimX+16,trimY+48,32,16)
@sprites["corner0"].src_rect.set(trimX, trimY + 0, 16, 16)
@sprites["corner1"].src_rect.set(trimX + 48, trimY + 0, 16, 16)
@sprites["corner2"].src_rect.set(trimX, trimY + 48, 16, 16)
@sprites["corner3"].src_rect.set(trimX + 48, trimY + 48, 16, 16)
@sprites["scroll0"].src_rect.set(trimX + 24, trimY + 16, 16, 8) # up
@sprites["scroll3"].src_rect.set(trimX + 24, trimY + 40, 16, 8) # down
@sprites["scroll1"].src_rect.set(trimX + 16, trimY + 24, 8, 16) # left
@sprites["scroll2"].src_rect.set(trimX + 40, trimY + 24, 8, 16) # right
cursorX = trimX
cursorY = trimY + 64
sideRects = [
Rect.new(trimX + 16, trimY + 0, 32, 16),
Rect.new(trimX, trimY + 16, 16, 32),
Rect.new(trimX + 48, trimY + 16, 16, 32),
Rect.new(trimX + 16, trimY + 48, 32, 16)
]
if @width>32 && @height>32
@sprites["contents"].src_rect.set(@ox,@oy,@width-32,@height-32)
if @width > 32 && @height > 32
@sprites["contents"].src_rect.set(@ox, @oy, @width - 32, @height - 32)
else
@sprites["contents"].src_rect.set(0,0,0,0)
@sprites["contents"].src_rect.set(0, 0, 0, 0)
end
pauseRects=[
trimX+32,trimY+64,
trimX+48,trimY+64,
trimX+32,trimY+80,
trimX+48,trimY+80,
pauseRects = [
trimX + 32, trimY + 64,
trimX + 48, trimY + 64,
trimX + 32, trimY + 80,
trimX + 48, trimY + 80
]
pauseWidth=16
pauseHeight=16
@sprites["pause"].src_rect.set(
pauseRects[@pauseframe*2],
pauseRects[@pauseframe*2+1],
pauseWidth,pauseHeight
)
@sprites["pause"].x=@x+(@width/2)-(pauseWidth/2)
@sprites["pause"].y=@y+@height-16 # 16 refers to skin margin
@sprites["contents"].x=@x+16
@sprites["contents"].y=@y+16
@sprites["corner0"].x=@x
@sprites["corner0"].y=@y
@sprites["corner1"].x=@x+@width-16
@sprites["corner1"].y=@y
@sprites["corner2"].x=@x
@sprites["corner2"].y=@y+@height-16
@sprites["corner3"].x=@x+@width-16
@sprites["corner3"].y=@y+@height-16
@sprites["side0"].x=@x+16
@sprites["side0"].y=@y
@sprites["side1"].x=@x
@sprites["side1"].y=@y+16
@sprites["side2"].x=@x+@width-16
@sprites["side2"].y=@y+16
@sprites["side3"].x=@x+16
@sprites["side3"].y=@y+@height-16
@sprites["scroll0"].x = @x+@width / 2 - 8
@sprites["scroll0"].y = @y+8
@sprites["scroll1"].x = @x+8
@sprites["scroll1"].y = @y+@height / 2 - 8
@sprites["scroll2"].x = @x+@width - 16
@sprites["scroll2"].y = @y+@height / 2 - 8
@sprites["scroll3"].x = @x+@width / 2 - 8
@sprites["scroll3"].y = @y+@height - 16
@sprites["back"].x=@x+2
@sprites["back"].y=@y+2
@sprites["cursor"].x=@x+16+@cursor_rect.x
@sprites["cursor"].y=@y+16+@cursor_rect.y
pauseWidth = 16
pauseHeight = 16
@sprites["pause"].src_rect.set(pauseRects[@pauseframe * 2],
pauseRects[(@pauseframe * 2) + 1],
pauseWidth,
pauseHeight)
@sprites["pause"].x = @x + (@width / 2) - (pauseWidth / 2)
@sprites["pause"].y = @y + @height - 16 # 16 refers to skin margin
@sprites["contents"].x = @x + 16
@sprites["contents"].y = @y + 16
@sprites["corner0"].x = @x
@sprites["corner0"].y = @y
@sprites["corner1"].x = @x + @width - 16
@sprites["corner1"].y = @y
@sprites["corner2"].x = @x
@sprites["corner2"].y = @y + @height - 16
@sprites["corner3"].x = @x + @width - 16
@sprites["corner3"].y = @y + @height - 16
@sprites["side0"].x = @x + 16
@sprites["side0"].y = @y
@sprites["side1"].x = @x
@sprites["side1"].y = @y + 16
@sprites["side2"].x = @x + @width - 16
@sprites["side2"].y = @y + 16
@sprites["side3"].x = @x + 16
@sprites["side3"].y = @y + @height - 16
@sprites["scroll0"].x = @x + (@width / 2) - 8
@sprites["scroll0"].y = @y + 8
@sprites["scroll1"].x = @x + 8
@sprites["scroll1"].y = @y + (@height / 2) - 8
@sprites["scroll2"].x = @x + @width - 16
@sprites["scroll2"].y = @y + (@height / 2) - 8
@sprites["scroll3"].x = @x + (@width / 2) - 8
@sprites["scroll3"].y = @y + @height - 16
@sprites["back"].x = @x + 2
@sprites["back"].y = @y + 2
@sprites["cursor"].x = @x + 16 + @cursor_rect.x
@sprites["cursor"].y = @y + 16 + @cursor_rect.y
if changeBitmap && @_windowskin && !@_windowskin.disposed?
width=@cursor_rect.width
height=@cursor_rect.height
width = @cursor_rect.width
height = @cursor_rect.height
if width > 0 && height > 0
cursorrects=[
# sides
Rect.new(cursorX+2, cursorY+0, 28, 2),
Rect.new(cursorX+0, cursorY+2, 2, 28),
Rect.new(cursorX+30, cursorY+2, 2, 28),
Rect.new(cursorX+2, cursorY+30, 28, 2),
# corners
Rect.new(cursorX+0, cursorY+0, 2, 2),
Rect.new(cursorX+30, cursorY+0, 2, 2),
Rect.new(cursorX+0, cursorY+30, 2, 2),
Rect.new(cursorX+30, cursorY+30, 2, 2),
# back
Rect.new(cursorX+2, cursorY+2, 28, 28)
cursorrects = [
# sides
Rect.new(cursorX + 2, cursorY + 0, 28, 2),
Rect.new(cursorX + 0, cursorY + 2, 2, 28),
Rect.new(cursorX + 30, cursorY + 2, 2, 28),
Rect.new(cursorX + 2, cursorY + 30, 28, 2),
# corners
Rect.new(cursorX + 0, cursorY + 0, 2, 2),
Rect.new(cursorX + 30, cursorY + 0, 2, 2),
Rect.new(cursorX + 0, cursorY + 30, 2, 2),
Rect.new(cursorX + 30, cursorY + 30, 2, 2),
# back
Rect.new(cursorX + 2, cursorY + 2, 28, 28)
]
margin=2
fullmargin=4
margin = 2
fullmargin = 4
@cursorbitmap = ensureBitmap(@cursorbitmap, width, height)
@cursorbitmap.clear
@sprites["cursor"].bitmap=@cursorbitmap
@sprites["cursor"].src_rect.set(0,0,width,height)
rect = Rect.new(margin,margin,
width - fullmargin, height - fullmargin)
@sprites["cursor"].bitmap = @cursorbitmap
@sprites["cursor"].src_rect.set(0, 0, width, height)
rect = Rect.new(margin, margin, width - fullmargin, height - fullmargin)
@cursorbitmap.stretch_blt(rect, @_windowskin, cursorrects[8])
@cursorbitmap.blt(0, 0, @_windowskin, cursorrects[4])# top left
@cursorbitmap.blt(width-margin, 0, @_windowskin, cursorrects[5]) # top right
@cursorbitmap.blt(0, height-margin, @_windowskin, cursorrects[6]) # bottom right
@cursorbitmap.blt(width-margin, height-margin, @_windowskin, cursorrects[7]) # bottom left
rect = Rect.new(margin, 0,
width - fullmargin, margin)
@cursorbitmap.blt(0, 0, @_windowskin, cursorrects[4]) # top left
@cursorbitmap.blt(width - margin, 0, @_windowskin, cursorrects[5]) # top right
@cursorbitmap.blt(0, height - margin, @_windowskin, cursorrects[6]) # bottom right
@cursorbitmap.blt(width - margin, height - margin, @_windowskin, cursorrects[7]) # bottom left
rect = Rect.new(margin, 0, width - fullmargin, margin)
@cursorbitmap.stretch_blt(rect, @_windowskin, cursorrects[0])
rect = Rect.new(0, margin,
margin, height - fullmargin)
rect = Rect.new(0, margin, margin, height - fullmargin)
@cursorbitmap.stretch_blt(rect, @_windowskin, cursorrects[1])
rect = Rect.new(width - margin, margin,
margin, height - fullmargin)
rect = Rect.new(width - margin, margin, margin, height - fullmargin)
@cursorbitmap.stretch_blt(rect, @_windowskin, cursorrects[2])
rect = Rect.new(margin, height-margin,
width - fullmargin, margin)
rect = Rect.new(margin, height - margin, width - fullmargin, margin)
@cursorbitmap.stretch_blt(rect, @_windowskin, cursorrects[3])
else
@sprites["cursor"].visible=false
@sprites["cursor"].src_rect.set(0,0,0,0)
@sprites["cursor"].visible = false
@sprites["cursor"].src_rect.set(0, 0, 0, 0)
end
for i in 0...4
dwidth = (i==0 || i==3) ? @width-32 : 16
dheight = (i==0 || i==3) ? 16 : @height-32
@sidebitmaps[i]=ensureBitmap(@sidebitmaps[i],dwidth,dheight)
@sprites["side#{i}"].bitmap=@sidebitmaps[i]
@sprites["side#{i}"].src_rect.set(0,0,dwidth,dheight)
4.times do |i|
dwidth = [0, 3].include?(i) ? @width - 32 : 16
dheight = [0, 3].include?(i) ? 16 : @height - 32
@sidebitmaps[i] = ensureBitmap(@sidebitmaps[i], dwidth, dheight)
@sprites["side#{i}"].bitmap = @sidebitmaps[i]
@sprites["side#{i}"].src_rect.set(0, 0, dwidth, dheight)
@sidebitmaps[i].clear
if sideRects[i].width>0 && sideRects[i].height>0
@sidebitmaps[i].stretch_blt(@sprites["side#{i}"].src_rect,
@_windowskin,sideRects[i])
if sideRects[i].width > 0 && sideRects[i].height > 0
@sidebitmaps[i].stretch_blt(@sprites["side#{i}"].src_rect, @_windowskin, sideRects[i])
end
end
backwidth=@width-4
backheight=@height-4
if backwidth>0 && backheight>0
@backbitmap=ensureBitmap(@backbitmap,backwidth,backheight)
@sprites["back"].bitmap=@backbitmap
@sprites["back"].src_rect.set(0,0,backwidth,backheight)
backwidth = @width - 4
backheight = @height - 4
if backwidth > 0 && backheight > 0
@backbitmap = ensureBitmap(@backbitmap, backwidth, backheight)
@sprites["back"].bitmap = @backbitmap
@sprites["back"].src_rect.set(0, 0, backwidth, backheight)
@backbitmap.clear
if @stretch
@backbitmap.stretch_blt(@sprites["back"].src_rect,@_windowskin,backRect)
@backbitmap.stretch_blt(@sprites["back"].src_rect, @_windowskin, backRect)
else
tileBitmap(@backbitmap,@sprites["back"].src_rect,@_windowskin,backRect)
tileBitmap(@backbitmap, @sprites["back"].src_rect, @_windowskin, backRect)
end
if blindsRect
tileBitmap(@backbitmap,@sprites["back"].src_rect,@_windowskin,blindsRect)
tileBitmap(@backbitmap, @sprites["back"].src_rect, @_windowskin, blindsRect)
end
else
@sprites["back"].visible=false
@sprites["back"].src_rect.set(0,0,0,0)
@sprites["back"].visible = false
@sprites["back"].src_rect.set(0, 0, 0, 0)
end
end
if @openness!=255
opn=@openness/255.0
for k in @spritekeys
sprite=@sprites[k]
ratio=(@height<=0) ? 0 : (sprite.y-@y)*1.0/@height
sprite.zoom_y=opn
sprite.oy=0
sprite.y=(@y+(@height/2.0)+(@height*ratio*opn)-(@height/2*opn)).floor
if @openness == 255
@spritekeys.each do |k|
sprite = @sprites[k]
sprite.zoom_y = 1.0
end
else
for k in @spritekeys
sprite=@sprites[k]
sprite.zoom_y=1.0
opn = @openness / 255.0
@spritekeys.each do |k|
sprite = @sprites[k]
ratio = (@height <= 0) ? 0 : (sprite.y - @y) / @height.to_f
sprite.zoom_y = opn
sprite.oy = 0
sprite.y = (@y + (@height / 2.0) + (@height * ratio * opn) - (@height / 2 * opn)).floor
end
end
i=0
i = 0
# Ensure Z order
for k in @spritekeys
sprite=@sprites[k]
y=sprite.y
sprite.y=i
sprite.oy=(sprite.zoom_y<=0) ? 0 : (i-y)/sprite.zoom_y
@spritekeys.each do |k|
sprite = @sprites[k]
y = sprite.y
sprite.y = i
sprite.oy = (sprite.zoom_y <= 0) ? 0 : (i - y) / sprite.zoom_y
end
end
end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -4,16 +4,16 @@
class IconWindow < SpriteWindow_Base
attr_reader :name
def initialize(x,y,width,height,viewport=nil)
super(x,y,width,height)
self.viewport=viewport
self.contents=nil
@name=""
@_iconbitmap=nil
def initialize(x, y, width, height, viewport = nil)
super(x, y, width, height)
self.viewport = viewport
self.contents = nil
@name = ""
@_iconbitmap = nil
end
def dispose
clearBitmaps()
clearBitmaps
super
end
@@ -21,14 +21,14 @@ class IconWindow < SpriteWindow_Base
super
if @_iconbitmap
@_iconbitmap.update
self.contents=@_iconbitmap.bitmap
self.contents = @_iconbitmap.bitmap
end
end
def clearBitmaps
@_iconbitmap.dispose if @_iconbitmap
@_iconbitmap=nil
self.contents=nil if !self.disposed?
@_iconbitmap&.dispose
@_iconbitmap = nil
self.contents = nil if !self.disposed?
end
# Sets the icon's filename. Alias for setBitmap.
@@ -37,37 +37,35 @@ class IconWindow < SpriteWindow_Base
end
# Sets the icon's filename.
def setBitmap(file,hue=0)
clearBitmaps()
@name=file
return if file==nil
if file!=""
@_iconbitmap=AnimatedBitmap.new(file,hue)
# for compatibility
self.contents=@_iconbitmap ? @_iconbitmap.bitmap : nil
def setBitmap(file, hue = 0)
clearBitmaps
@name = file
return if file.nil?
if file == ""
@_iconbitmap = nil
else
@_iconbitmap=nil
@_iconbitmap = AnimatedBitmap.new(file, hue)
# for compatibility
self.contents = @_iconbitmap ? @_iconbitmap.bitmap : nil
end
end
end
#===============================================================================
# Displays an icon bitmap in a window. Supports animated images.
# Accepts bitmaps and paths to bitmap files in its constructor.
#===============================================================================
class PictureWindow < SpriteWindow_Base
def initialize(pathOrBitmap)
super(0,0,32,32)
self.viewport=viewport
self.contents=nil
@_iconbitmap=nil
super(0, 0, 32, 32)
self.viewport = viewport
self.contents = nil
@_iconbitmap = nil
setBitmap(pathOrBitmap)
end
def dispose
clearBitmaps()
clearBitmaps
super
end
@@ -75,47 +73,46 @@ class PictureWindow < SpriteWindow_Base
super
if @_iconbitmap
if @_iconbitmap.is_a?(Bitmap)
self.contents=@_iconbitmap
self.contents = @_iconbitmap
else
@_iconbitmap.update
self.contents=@_iconbitmap.bitmap
self.contents = @_iconbitmap.bitmap
end
end
end
def clearBitmaps
@_iconbitmap.dispose if @_iconbitmap
@_iconbitmap=nil
self.contents=nil if !self.disposed?
@_iconbitmap&.dispose
@_iconbitmap = nil
self.contents = nil if !self.disposed?
end
# Sets the icon's bitmap or filename. (hue parameter
# is ignored unless pathOrBitmap is a filename)
def setBitmap(pathOrBitmap,hue=0)
clearBitmaps()
if pathOrBitmap!=nil && pathOrBitmap!=""
if pathOrBitmap.is_a?(Bitmap)
@_iconbitmap=pathOrBitmap
self.contents=@_iconbitmap
self.width=@_iconbitmap.width+self.borderX
self.height=@_iconbitmap.height+self.borderY
elsif pathOrBitmap.is_a?(AnimatedBitmap)
@_iconbitmap=pathOrBitmap
self.contents=@_iconbitmap.bitmap
self.width=@_iconbitmap.bitmap.width+self.borderX
self.height=@_iconbitmap.bitmap.height+self.borderY
def setBitmap(pathOrBitmap, hue = 0)
clearBitmaps
if pathOrBitmap && pathOrBitmap != ""
case pathOrBitmap
when Bitmap
@_iconbitmap = pathOrBitmap
self.contents = @_iconbitmap
self.width = @_iconbitmap.width + self.borderX
self.height = @_iconbitmap.height + self.borderY
when AnimatedBitmap
@_iconbitmap = pathOrBitmap
self.contents = @_iconbitmap.bitmap
self.width = @_iconbitmap.bitmap.width + self.borderX
self.height = @_iconbitmap.bitmap.height + self.borderY
else
@_iconbitmap=AnimatedBitmap.new(pathOrBitmap,hue)
self.contents=@_iconbitmap ? @_iconbitmap.bitmap : nil
self.width=@_iconbitmap ? @_iconbitmap.bitmap.width+self.borderX :
32+self.borderX
self.height=@_iconbitmap ? @_iconbitmap.bitmap.height+self.borderY :
32+self.borderY
@_iconbitmap = AnimatedBitmap.new(pathOrBitmap, hue)
self.contents = @_iconbitmap&.bitmap
self.width = self.borderX + (@_iconbitmap&.bitmap&.width || 32)
self.height = self.borderY + (@_iconbitmap&.bitmap&.height || 32)
end
else
@_iconbitmap=nil
self.width=32+self.borderX
self.height=32+self.borderY
@_iconbitmap = nil
self.width = 32 + self.borderX
self.height = 32 + self.borderY
end
end
end

View File

@@ -0,0 +1,371 @@
#===============================================================================
# Sprite class that maintains a bitmap of its own.
# This bitmap can't be changed to a different one.
#===============================================================================
class BitmapSprite < Sprite
attr_reader :text_themes
def initialize(width, height, viewport = nil)
super(viewport)
self.bitmap = Bitmap.new(width, height)
@text_themes = {}
@initialized = true
end
def dispose
self.bitmap.dispose if !self.disposed?
super
end
def bitmap=(value)
super(value) if !@initialized
end
#-----------------------------------------------------------------------------
def add_text_theme(id, base_color, shadow_color = nil)
@text_themes[id] = [base_color, shadow_color]
end
# TODO: Replaces def pbDrawTextPositions.
def draw_themed_text(string, text_x, text_y, align = :left, theme = :default, outline = :shadow)
string_size = self.bitmap.text_size(string)
case align
when :right
text_x -= string_size.width
when :center
text_x -= (string_size.width / 2)
end
if !@text_themes[theme]
theme = (@text_themes[:default]) ? :default : @text_themes.keys.first
end
case outline || :shadow
when :shadow
draw_shadowed_text(string, text_x, text_y, theme)
when :outline
draw_outlined_text(string, text_x, text_y, theme)
when :none
draw_plain_text(string, text_x, text_y, theme)
end
end
# TODO: Replaces def pbDrawShadowText.
def draw_shadowed_text(string, text_x, text_y, theme)
return if !@text_themes[theme]
base_color, shadow_color = @text_themes[theme]
string_size = self.bitmap.text_size(string)
string_width = string_size.width + 1
string_height = string_size.height + 1
if shadow_color && shadow_color.alpha > 0
self.bitmap.font.color = shadow_color
self.bitmap.draw_text(text_x + 2, text_y, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x, text_y + 2, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x + 2, text_y + 2, string_width, string_height, string, 0)
end
if base_color && base_color.alpha > 0
self.bitmap.font.color = base_color
self.bitmap.draw_text(text_x, text_y, string_width, string_height, string, 0)
end
end
# TODO: Replaces def pbDrawOutlineText.
def draw_outlined_text(string, text_x, text_y, theme)
return if !@text_themes[theme]
base_color, shadow_color = @text_themes[theme]
string_size = self.bitmap.text_size(string)
string_width = string_size.width + 1
string_height = string_size.height + 1
if shadow_color && shadow_color.alpha > 0
self.bitmap.font.color = shadow_color
self.bitmap.draw_text(text_x - 2, text_y - 2, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x, text_y - 2, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x + 2, text_y - 2, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x - 2, text_y, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x + 2, text_y, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x - 2, text_y + 2, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x, text_y + 2, string_width, string_height, string, 0)
self.bitmap.draw_text(text_x + 2, text_y + 2, string_width, string_height, string, 0)
end
if base_color && base_color.alpha > 0
self.bitmap.font.color = base_color
self.bitmap.draw_text(text_x, text_y, string_width, string_height, string, 0)
end
end
# TODO: Replaces def pbDrawPlainText.
def draw_plain_text(string, text_x, text_y, theme)
return if !@text_themes[theme]
base_color = @text_themes[theme][0]
return if !base_color || base_color.alpha == 0
string_size = self.bitmap.text_size(string)
string_width = string_size.width + 1
string_height = string_size.height + 1
self.bitmap.font.color = base_color
self.bitmap.draw_text(text_x, text_y, string_width, string_height, string, 0)
end
#-----------------------------------------------------------------------------
# TODO: Replaces def pbDrawImagePositions.
def draw_image(filename, image_x, image_y, src_x = 0, src_y = 0, src_width = -1, src_height = -1)
src_bitmap = (filename.is_a?(AnimatedBitmap)) ? filename : AnimatedBitmap.new(pbBitmapName(filename))
src_width = (src_width >= 0) ? src_width : src_bitmap.width
src_height = (src_height >= 0) ? src_height : src_bitmap.height
src_rect = Rect.new(src_x, src_y, src_width, src_height)
self.bitmap.blt(image_x, image_y, src_bitmap.bitmap, src_rect)
src_bitmap.dispose if !filename.is_a?(AnimatedBitmap)
end
end
#===============================================================================
#
#===============================================================================
class AnimatedSprite < Sprite
attr_reader :frame
attr_reader :framewidth
attr_reader :frameheight
attr_reader :framecount
attr_reader :animname
# frameskip is in 1/20ths of a second, and is the time between frame changes.
def initializeLong(animname, framecount, framewidth, frameheight, frameskip)
@animname = pbBitmapName(animname)
@time_per_frame = [1, frameskip].max / 20.0
raise _INTL("Frame width is 0") if framewidth == 0
raise _INTL("Frame height is 0") if frameheight == 0
begin
@animbitmap = AnimatedBitmap.new(animname).deanimate
rescue
@animbitmap = Bitmap.new(framewidth, frameheight)
end
if @animbitmap.width % framewidth != 0
raise _INTL("Bitmap's width ({1}) is not a multiple of frame width ({2}) [Bitmap={3}]",
@animbitmap.width, framewidth, animname)
end
if @animbitmap.height % frameheight != 0
raise _INTL("Bitmap's height ({1}) is not a multiple of frame height ({2}) [Bitmap={3}]",
@animbitmap.height, frameheight, animname)
end
@framecount = framecount
@framewidth = framewidth
@frameheight = frameheight
@framesperrow = @animbitmap.width / @framewidth
@playing = false
self.bitmap = @animbitmap
self.src_rect.width = @framewidth
self.src_rect.height = @frameheight
self.frame = 0
end
# Shorter version of AnimatedSprite. All frames are placed on a single row
# of the bitmap, so that the width and height need not be defined beforehand.
# frameskip is in 1/20ths of a second, and is the time between frame changes.
def initializeShort(animname, framecount, frameskip)
@animname = pbBitmapName(animname)
@time_per_frame = [1, frameskip].max / 20.0
begin
@animbitmap = AnimatedBitmap.new(animname).deanimate
rescue
@animbitmap = Bitmap.new(framecount * 4, 32)
end
if @animbitmap.width % framecount != 0
raise _INTL("Bitmap's width ({1}) is not a multiple of frame count ({2}) [Bitmap={3}]",
@animbitmap.width, framewidth, animname)
end
@framecount = framecount
@framewidth = @animbitmap.width / @framecount
@frameheight = @animbitmap.height
@framesperrow = framecount
@playing = false
self.bitmap = @animbitmap
self.src_rect.width = @framewidth
self.src_rect.height = @frameheight
self.frame = 0
end
def initialize(*args)
if args.length == 1
super(args[0][3])
initializeShort(args[0][0], args[0][1], args[0][2])
else
super(args[5])
initializeLong(args[0], args[1], args[2], args[3], args[4])
end
end
def self.create(animname, framecount, frameskip, viewport = nil)
return self.new([animname, framecount, frameskip, viewport])
end
def dispose
return if disposed?
@animbitmap.dispose
@animbitmap = nil
super
end
def playing?
return @playing
end
def frame=(value)
@frame = value
self.src_rect.x = @frame % @framesperrow * @framewidth
self.src_rect.y = @frame / @framesperrow * @frameheight
end
def start
@playing = true
end
alias play start
def stop
@playing = false
end
def update
super
if @playing
new_frame = (System.uptime / @time_per_frame).to_i % self.framecount
self.frame = new_frame if self.frame != new_frame
end
end
end
#===============================================================================
# Displays an icon bitmap in a sprite. Supports animated images.
#===============================================================================
class IconSprite < Sprite
attr_reader :name
def initialize(*args)
case args.length
when 0
super(nil)
self.bitmap = nil
when 1
super(args[0])
self.bitmap = nil
when 2
super(nil)
self.x = args[0]
self.y = args[1]
else
super(args[2])
self.x = args[0]
self.y = args[1]
end
@name = ""
@_iconbitmap = nil
end
def dispose
clearBitmaps
super
end
# Sets the icon's filename. Alias for setBitmap.
def name=(value)
setBitmap(value)
end
# Sets the icon's filename.
def setBitmap(file, hue = 0)
oldrc = self.src_rect
clearBitmaps
@name = file
return if file.nil?
if file == ""
@_iconbitmap = nil
else
@_iconbitmap = AnimatedBitmap.new(file, hue)
# for compatibility
self.bitmap = @_iconbitmap ? @_iconbitmap.bitmap : nil
self.src_rect = oldrc
end
end
def clearBitmaps
@_iconbitmap&.dispose
@_iconbitmap = nil
self.bitmap = nil if !self.disposed?
end
def update
super
return if !@_iconbitmap
@_iconbitmap.update
if self.bitmap != @_iconbitmap.bitmap
oldrc = self.src_rect
self.bitmap = @_iconbitmap.bitmap
self.src_rect = oldrc
end
end
end
#===============================================================================
# Sprite class that stores multiple bitmaps, and displays only one at once.
#===============================================================================
class ChangelingSprite < Sprite
# Key is the mode (a symbol).
# Value is one of:
# filepath
# [filepath, src_x, src_y, src_width, src_height]
BITMAPS = {}
def initialize(x = 0, y = 0, viewport = nil)
super(viewport)
self.x = x
self.y = y
@bitmaps = {}
@changeling_data = {}
@current_bitmap = nil
initialize_changeling_data
end
def initialize_changeling_data
self.class::BITMAPS.each_pair { |mode, data| add_bitmap(mode, data) }
end
def dispose
return if disposed?
@bitmaps.each_value { |bm| bm.dispose }
@bitmaps.clear
super
end
#-----------------------------------------------------------------------------
def add_bitmap(mode, *data)
raise ArgumentError.new(_INTL("wrong number of arguments (given {1}, expected 2 or 6)", data.length + 1)) if ![1, 5].include?(data.length)
filepath = (data[0].is_a?(Array)) ? data[0][0] : data[0]
@bitmaps[filepath] = AnimatedBitmap.new(filepath) if !@bitmaps[filepath]
@changeling_data[mode] = (data[0].is_a?(Array) ? data[0].clone : [data[0]])
end
def change_bitmap(mode)
@current_mode = mode
if @current_mode && @changeling_data[@current_mode]
data = @changeling_data[@current_mode]
@current_bitmap = @bitmaps[data[0]]
self.bitmap = @current_bitmap.bitmap
if data.length > 1
self.src_rect.set(data[1], data[2], data[3], data[4])
else
self.src_rect.set(0, 0, self.bitmap.width, self.bitmap.height)
end
else
@current_bitmap = nil
self.bitmap = nil
end
end
#-----------------------------------------------------------------------------
def update
return if disposed?
@bitmaps.each_value { |bm| bm.update }
self.bitmap = @current_bitmap.bitmap if @current_bitmap
end
end

View File

@@ -1,359 +0,0 @@
#===============================================================================
# SpriteWrapper is a class which wraps (most of) Sprite's properties.
#===============================================================================
class SpriteWrapper
def initialize(viewport=nil)
@sprite = Sprite.new(viewport)
end
def dispose; @sprite.dispose; end
def disposed?; return @sprite.disposed?; end
def viewport; return @sprite.viewport; end
def flash(color,duration); return @sprite.flash(color,duration); end
def update; return @sprite.update; end
def x; @sprite.x; end
def x=(value); @sprite.x = value; end
def y; @sprite.y; end
def y=(value); @sprite.y = value; end
def bitmap; @sprite.bitmap; end
def bitmap=(value); @sprite.bitmap = value; end
def src_rect; @sprite.src_rect; end
def src_rect=(value); @sprite.src_rect = value; end
def visible; @sprite.visible; end
def visible=(value); @sprite.visible = value; end
def z; @sprite.z; end
def z=(value); @sprite.z = value; end
def ox; @sprite.ox; end
def ox=(value); @sprite.ox = value; end
def oy; @sprite.oy; end
def oy=(value); @sprite.oy = value; end
def zoom_x; @sprite.zoom_x; end
def zoom_x=(value); @sprite.zoom_x = value; end
def zoom_y; @sprite.zoom_y; end
def zoom_y=(value); @sprite.zoom_y = value; end
def angle; @sprite.angle; end
def angle=(value); @sprite.angle = value; end
def mirror; @sprite.mirror; end
def mirror=(value); @sprite.mirror = value; end
def bush_depth; @sprite.bush_depth; end
def bush_depth=(value); @sprite.bush_depth = value; end
def opacity; @sprite.opacity; end
def opacity=(value); @sprite.opacity = value; end
def blend_type; @sprite.blend_type; end
def blend_type=(value); @sprite.blend_type = value; end
def color; @sprite.color; end
def color=(value); @sprite.color = value; end
def tone; @sprite.tone; end
def tone=(value); @sprite.tone = value; end
def viewport=(value)
return if self.viewport==value
bitmap = @sprite.bitmap
src_rect = @sprite.src_rect
visible = @sprite.visible
x = @sprite.x
y = @sprite.y
z = @sprite.z
ox = @sprite.ox
oy = @sprite.oy
zoom_x = @sprite.zoom_x
zoom_y = @sprite.zoom_y
angle = @sprite.angle
mirror = @sprite.mirror
bush_depth = @sprite.bush_depth
opacity = @sprite.opacity
blend_type = @sprite.blend_type
color = @sprite.color
tone = @sprite.tone
@sprite.dispose
@sprite = Sprite.new(value)
@sprite.bitmap = bitmap
@sprite.src_rect = src_rect
@sprite.visible = visible
@sprite.x = x
@sprite.y = y
@sprite.z = z
@sprite.ox = ox
@sprite.oy = oy
@sprite.zoom_x = zoom_x
@sprite.zoom_y = zoom_y
@sprite.angle = angle
@sprite.mirror = mirror
@sprite.bush_depth = bush_depth
@sprite.opacity = opacity
@sprite.blend_type = blend_type
@sprite.color = color
@sprite.tone = tone
end
end
#===============================================================================
# Sprite class that maintains a bitmap of its own.
# This bitmap can't be changed to a different one.
#===============================================================================
class BitmapSprite < SpriteWrapper
def initialize(width,height,viewport=nil)
super(viewport)
self.bitmap=Bitmap.new(width,height)
@initialized=true
end
def bitmap=(value)
super(value) if !@initialized
end
def dispose
self.bitmap.dispose if !self.disposed?
super
end
end
#===============================================================================
#
#===============================================================================
class AnimatedSprite < SpriteWrapper
attr_reader :frame
attr_reader :framewidth
attr_reader :frameheight
attr_reader :framecount
attr_reader :animname
def initializeLong(animname,framecount,framewidth,frameheight,frameskip)
@animname=pbBitmapName(animname)
@realframes=0
@frameskip=[1,frameskip].max
@frameskip *= Graphics.frame_rate/20
raise _INTL("Frame width is 0") if framewidth==0
raise _INTL("Frame height is 0") if frameheight==0
begin
@animbitmap=AnimatedBitmap.new(animname).deanimate
rescue
@animbitmap=Bitmap.new(framewidth,frameheight)
end
if @animbitmap.width%framewidth!=0
raise _INTL("Bitmap's width ({1}) is not a multiple of frame width ({2}) [Bitmap={3}]",
@animbitmap.width,framewidth,animname)
end
if @animbitmap.height%frameheight!=0
raise _INTL("Bitmap's height ({1}) is not a multiple of frame height ({2}) [Bitmap={3}]",
@animbitmap.height,frameheight,animname)
end
@framecount=framecount
@framewidth=framewidth
@frameheight=frameheight
@framesperrow=@animbitmap.width/@framewidth
@playing=false
self.bitmap=@animbitmap
self.src_rect.width=@framewidth
self.src_rect.height=@frameheight
self.frame=0
end
# Shorter version of AnimationSprite. All frames are placed on a single row
# of the bitmap, so that the width and height need not be defined beforehand
def initializeShort(animname,framecount,frameskip)
@animname=pbBitmapName(animname)
@realframes=0
@frameskip=[1,frameskip].max
@frameskip *= Graphics.frame_rate/20
begin
@animbitmap=AnimatedBitmap.new(animname).deanimate
rescue
@animbitmap=Bitmap.new(framecount*4,32)
end
if @animbitmap.width%framecount!=0
raise _INTL("Bitmap's width ({1}) is not a multiple of frame count ({2}) [Bitmap={3}]",
@animbitmap.width,framewidth,animname)
end
@framecount=framecount
@framewidth=@animbitmap.width/@framecount
@frameheight=@animbitmap.height
@framesperrow=framecount
@playing=false
self.bitmap=@animbitmap
self.src_rect.width=@framewidth
self.src_rect.height=@frameheight
self.frame=0
end
def initialize(*args)
if args.length==1
super(args[0][3])
initializeShort(args[0][0],args[0][1],args[0][2])
else
super(args[5])
initializeLong(args[0],args[1],args[2],args[3],args[4])
end
end
def self.create(animname,framecount,frameskip,viewport=nil)
return self.new([animname,framecount,frameskip,viewport])
end
def dispose
return if disposed?
@animbitmap.dispose
@animbitmap=nil
super
end
def playing?
return @playing
end
def frame=(value)
@frame=value
@realframes=0
self.src_rect.x=@frame%@framesperrow*@framewidth
self.src_rect.y=@frame/@framesperrow*@frameheight
end
def start
@playing=true
@realframes=0
end
alias play start
def stop
@playing=false
end
def update
super
if @playing
@realframes+=1
if @realframes==@frameskip
@realframes=0
self.frame+=1
self.frame%=self.framecount
end
end
end
end
#===============================================================================
# Displays an icon bitmap in a sprite. Supports animated images.
#===============================================================================
class IconSprite < SpriteWrapper
attr_reader :name
def initialize(*args)
if args.length==0
super(nil)
self.bitmap=nil
elsif args.length==1
super(args[0])
self.bitmap=nil
elsif args.length==2
super(nil)
self.x=args[0]
self.y=args[1]
else
super(args[2])
self.x=args[0]
self.y=args[1]
end
@name=""
@_iconbitmap=nil
end
def dispose
clearBitmaps()
super
end
# Sets the icon's filename. Alias for setBitmap.
def name=(value)
setBitmap(value)
end
# Sets the icon's filename.
def setBitmap(file,hue=0)
oldrc=self.src_rect
clearBitmaps()
@name=file
return if file==nil
if file!=""
@_iconbitmap=AnimatedBitmap.new(file,hue)
# for compatibility
self.bitmap=@_iconbitmap ? @_iconbitmap.bitmap : nil
self.src_rect=oldrc
else
@_iconbitmap=nil
end
end
def clearBitmaps
@_iconbitmap.dispose if @_iconbitmap
@_iconbitmap=nil
self.bitmap=nil if !self.disposed?
end
def update
super
return if !@_iconbitmap
@_iconbitmap.update
if self.bitmap!=@_iconbitmap.bitmap
oldrc=self.src_rect
self.bitmap=@_iconbitmap.bitmap
self.src_rect=oldrc
end
end
end
#===============================================================================
# Old GifSprite class, retained for compatibility
#===============================================================================
class GifSprite < IconSprite
def initialize(path)
super(0,0)
setBitmap(path)
end
end
#===============================================================================
# SpriteWrapper that stores multiple bitmaps, and displays only one at once.
#===============================================================================
class ChangelingSprite < SpriteWrapper
def initialize(x=0,y=0,viewport=nil)
super(viewport)
self.x = x
self.y = y
@bitmaps = {}
@currentBitmap = nil
end
def addBitmap(key,path)
@bitmaps[key].dispose if @bitmaps[key]
@bitmaps[key] = AnimatedBitmap.new(path)
end
def changeBitmap(key)
@currentBitmap = @bitmaps[key]
self.bitmap = (@currentBitmap) ? @currentBitmap.bitmap : nil
end
def dispose
return if disposed?
for bm in @bitmaps.values; bm.dispose; end
@bitmaps.clear
super
end
def update
return if disposed?
for bm in @bitmaps.values; bm.update; end
self.bitmap = (@currentBitmap) ? @currentBitmap.bitmap : nil
end
end

View File

@@ -5,11 +5,13 @@ class AnimatedBitmap
def initialize(file, hue = 0)
raise "Filename is nil (missing graphic)." if file.nil?
path = file
@path = path
filename = ""
if file.last != '/' # Isn't just a directory
if file.last != "/" # Isn't just a directory
split_file = file.split(/[\\\/]/)
filename = split_file.pop
path = split_file.join('/') + '/'
path = split_file.join("/") + "/"
end
if filename[/^\[\d+(?:,\d+)?\]/] # Starts with 1 or 2 numbers in square brackets
@bitmap = PngAnimatedBitmap.new(path, filename, hue)
@@ -31,6 +33,8 @@ class AnimatedBitmap
def dispose; @bitmap.dispose; end
def deanimate; @bitmap.deanimate; end
def copy; @bitmap.copy; end
def path; @path end
end
#===============================================================================
@@ -43,22 +47,22 @@ class PngAnimatedBitmap
def initialize(dir, filename, hue = 0)
@frames = []
@currentFrame = 0
@framecount = 0
@timer_start = System.uptime
panorama = RPG::Cache.load_bitmap(dir, filename, hue)
if filename[/^\[(\d+)(?:,(\d+))?\]/] # Starts with 1 or 2 numbers in brackets
# File has a frame count
numFrames = $1.to_i
delay = $2.to_i
delay = 10 if delay == 0
duration = $2.to_i # In 1/20ths of a second
duration = 5 if duration == 0
raise "Invalid frame count in #{filename}" if numFrames <= 0
raise "Invalid frame delay in #{filename}" if delay <= 0
raise "Invalid frame duration in #{filename}" if duration <= 0
if panorama.width % numFrames != 0
raise "Bitmap's width (#{panorama.width}) is not divisible by frame count: #{filename}"
end
@frameDelay = delay
@frame_duration = duration / 20.0
subWidth = panorama.width / numFrames
for i in 0...numFrames
subBitmap = BitmapWrapper.new(subWidth, panorama.height)
numFrames.times do |i|
subBitmap = Bitmap.new(subWidth, panorama.height)
subBitmap.blt(0, 0, panorama, Rect.new(subWidth * i, 0, subWidth, panorama.height))
@frames.push(subBitmap)
end
@@ -68,6 +72,16 @@ class PngAnimatedBitmap
end
end
def dispose
return if @disposed
@frames.each { |f| f.dispose }
@disposed = true
end
def disposed?
return @disposed
end
def [](index)
return @frames[index]
end
@@ -75,15 +89,6 @@ class PngAnimatedBitmap
def width; self.bitmap.width; end
def height; self.bitmap.height; end
def deanimate
for i in 1...@frames.length
@frames[i].dispose
end
@frames = [@frames[0]]
@currentFrame = 0
return @frames[0]
end
def bitmap
return @frames[@currentFrame]
end
@@ -92,53 +97,42 @@ class PngAnimatedBitmap
return @currentFrame
end
def frameDelay(_index)
return @frameDelay
end
def length
return @frames.length
end
# Actually returns the total number of 1/20ths of a second this animation lasts.
def totalFrames
return (@frame_duration * @frames.length * 20).to_i
end
def each
@frames.each { |item| yield item }
end
def totalFrames
return @frameDelay * @frames.length
end
def disposed?
return @disposed
end
def update
return if disposed?
if @frames.length > 1
@framecount += 1
if @framecount >= @frameDelay
@framecount = 0
@currentFrame += 1
@currentFrame %= @frames.length
end
def deanimate
(1...@frames.length).each do |i|
@frames[i].dispose
end
end
def dispose
if !@disposed
@frames.each { |f| f.dispose }
end
@disposed = true
@frames = [@frames[0]]
@currentFrame = 0
@frame_duration = 0
return @frames[0]
end
def copy
x = self.clone
x.frames = x.frames.clone
for i in 0...x.frames.length
x.frames[i] = x.frames[i].copy
end
x.frames.each_with_index { |frame, i| x.frames[i] = frame.copy }
return x
end
def update
return if disposed?
if @frames.length > 1
@currentFrame = ((System.uptime - @timer_start) / @frame_duration).to_i % @frames.length
end
end
end
#===============================================================================
@@ -157,7 +151,7 @@ class GifBitmap
rescue
@bitmap = nil
end
@bitmap = BitmapWrapper.new(32, 32) if @bitmap.nil?
@bitmap = Bitmap.new(32, 32) if @bitmap.nil?
@bitmap.play if @bitmap&.animated?
end
@@ -220,19 +214,19 @@ end
#
#===============================================================================
def pbGetTileBitmap(filename, tile_id, hue, width = 1, height = 1)
return RPG::Cache.tileEx(filename, tile_id, hue, width, height) { |f|
return RPG::Cache.tileEx(filename, tile_id, hue, width, height) do |f|
AnimatedBitmap.new("Graphics/Tilesets/" + filename).deanimate
}
end
end
def pbGetTileset(name,hue=0)
def pbGetTileset(name, hue = 0)
return AnimatedBitmap.new("Graphics/Tilesets/" + name, hue).deanimate
end
def pbGetAutotile(name,hue=0)
def pbGetAutotile(name, hue = 0)
return AnimatedBitmap.new("Graphics/Autotiles/" + name, hue).deanimate
end
def pbGetAnimation(name,hue=0)
def pbGetAnimation(name, hue = 0)
return AnimatedBitmap.new("Graphics/Animations/" + name, hue).deanimate
end

View File

@@ -6,225 +6,71 @@ class Plane
def refresh; end
end
#===============================================================================
# This class works around a limitation that planes are always
# 640 by 480 pixels in size regardless of the window's size.
#===============================================================================
class LargePlane < Plane
attr_accessor :borderX
attr_accessor :borderY
def initialize(viewport=nil)
@__sprite=Sprite.new(viewport)
@__disposed=false
@__ox=0
@__oy=0
@__bitmap=nil
@__visible=true
@__sprite.visible=false
@borderX=0
@borderY=0
end
def disposed?
return @__disposed
end
def dispose
if !@__disposed
@__sprite.bitmap.dispose if @__sprite.bitmap
@__sprite.dispose
@__sprite=nil
@__bitmap=nil
@__disposed=true
end
#super
end
def ox; @__ox; end
def oy; @__oy; end
def ox=(value);
return if @__ox==value
@__ox = value
refresh
end
def oy=(value);
return if @__oy==value
@__oy = value
refresh
end
def bitmap
return @__bitmap
end
def bitmap=(value)
if value==nil
if @__bitmap!=nil
@__bitmap=nil
@__sprite.visible=(@__visible && !@__bitmap.nil?)
end
elsif @__bitmap!=value && !value.disposed?
@__bitmap=value
refresh
elsif value.disposed?
if @__bitmap!=nil
@__bitmap=nil
@__sprite.visible=(@__visible && !@__bitmap.nil?)
end
end
end
def viewport; @__sprite.viewport; end
def zoom_x; @__sprite.zoom_x; end
def zoom_y; @__sprite.zoom_y; end
def opacity; @__sprite.opacity; end
def blend_type; @__sprite.blend_type; end
def visible; @__visible; end
def z; @__sprite.z; end
def color; @__sprite.color; end
def tone; @__sprite.tone; end
def zoom_x=(v);
return if @__sprite.zoom_x==v
@__sprite.zoom_x = v
refresh
end
def zoom_y=(v);
return if @__sprite.zoom_y==v
@__sprite.zoom_y = v
refresh
end
def opacity=(v); @__sprite.opacity=(v); end
def blend_type=(v); @__sprite.blend_type=(v); end
def visible=(v); @__visible=v; @__sprite.visible=(@__visible && !@__bitmap.nil?); end
def z=(v); @__sprite.z=(v); end
def color=(v); @__sprite.color=(v); end
def tone=(v); @__sprite.tone=(v); end
def update; ;end
def refresh
@__sprite.visible = (@__visible && !@__bitmap.nil?)
if @__bitmap
if !@__bitmap.disposed?
@__ox += @__bitmap.width*@__sprite.zoom_x if @__ox<0
@__oy += @__bitmap.height*@__sprite.zoom_y if @__oy<0
@__ox -= @__bitmap.width*@__sprite.zoom_x if @__ox>@__bitmap.width
@__oy -= @__bitmap.height*@__sprite.zoom_y if @__oy>@__bitmap.height
dwidth = (Graphics.width/@__sprite.zoom_x+@borderX).to_i # +2
dheight = (Graphics.height/@__sprite.zoom_y+@borderY).to_i # +2
@__sprite.bitmap = ensureBitmap(@__sprite.bitmap,dwidth,dheight)
@__sprite.bitmap.clear
tileBitmap(@__sprite.bitmap,@__bitmap,@__bitmap.rect)
else
@__sprite.visible = false
end
end
end
private
def ensureBitmap(bitmap,dwidth,dheight)
if !bitmap || bitmap.disposed? || bitmap.width<dwidth || bitmap.height<dheight
bitmap.dispose if bitmap
bitmap = Bitmap.new([1,dwidth].max,[1,dheight].max)
end
return bitmap
end
def tileBitmap(dstbitmap,srcbitmap,srcrect)
return if !srcbitmap || srcbitmap.disposed?
dstrect = dstbitmap.rect
left = (dstrect.x-@__ox/@__sprite.zoom_x).to_i
top = (dstrect.y-@__oy/@__sprite.zoom_y).to_i
while left>0; left -= srcbitmap.width; end
while top>0; top -= srcbitmap.height; end
y = top
while y<dstrect.height
x = left
while x<dstrect.width
dstbitmap.blt(x+@borderX,y+@borderY,srcbitmap,srcrect)
x += srcrect.width
end
y += srcrect.height
end
end
end
#===============================================================================
# A plane class that displays a single color.
#===============================================================================
class ColoredPlane < LargePlane
def initialize(color,viewport=nil)
class ColoredPlane < Plane
def initialize(color, viewport = nil)
super(viewport)
self.bitmap=Bitmap.new(32,32)
setPlaneColor(color)
self.bitmap = Bitmap.new(32, 32)
set_plane_color(color)
end
def dispose
self.bitmap.dispose if self.bitmap
self.bitmap&.dispose
super
end
def setPlaneColor(value)
self.bitmap.fill_rect(0,0,self.bitmap.width,self.bitmap.height,value)
self.refresh
def set_plane_color(value)
self.bitmap.fill_rect(0, 0, self.bitmap.width, self.bitmap.height, value)
refresh
end
end
#===============================================================================
# A plane class that supports animated images.
#===============================================================================
class AnimatedPlane < LargePlane
class AnimatedPlane < Plane
def initialize(viewport)
super(viewport)
@bitmap=nil
@bitmap = nil
end
def dispose
clearBitmaps()
clear_bitmap
super
end
def update
super
if @bitmap
@bitmap.update
self.bitmap=@bitmap.bitmap
def setBitmap(file, hue = 0)
clear_bitmap
return if file.nil?
@bitmap = AnimatedBitmap.new(file, hue)
self.bitmap = @bitmap.bitmap if @bitmap
end
def set_panorama(file, hue = 0)
if file.is_a?(String) && file.length > 0
setBitmap("Graphics/Panoramas/" + file, hue)
else
clear_bitmap
end
end
def clearBitmaps
@bitmap.dispose if @bitmap
@bitmap=nil
self.bitmap=nil if !self.disposed?
def set_fog(file, hue = 0)
if file.is_a?(String) && file.length > 0
setBitmap("Graphics/Fogs/" + file, hue)
else
clear_bitmap
end
end
def setPanorama(file, hue=0)
clearBitmaps()
return if file==nil
@bitmap=AnimatedBitmap.new("Graphics/Panoramas/"+file,hue)
end
#-----------------------------------------------------------------------------
def setFog(file, hue=0)
clearBitmaps()
return if file==nil
@bitmap=AnimatedBitmap.new("Graphics/Fogs/"+file,hue)
end
private
def setBitmap(file, hue=0)
clearBitmaps()
return if file==nil
@bitmap=AnimatedBitmap.new(file,hue)
def clear_bitmap
@bitmap&.dispose
@bitmap = nil
self.bitmap = nil if !self.disposed?
end
end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -2,32 +2,28 @@
#
#===============================================================================
class CharacterEntryHelper
attr_reader :text
attr_accessor :text
attr_accessor :maxlength
attr_reader :passwordChar
attr_accessor :cursor
def initialize(text)
@maxlength=-1
@text=text
@passwordChar=""
@cursor=text.scan(/./m).length
end
def text=(value)
@text=value
@maxlength = -1
@text = text
@passwordChar = ""
@cursor = text.scan(/./m).length
end
def textChars
chars=text.scan(/./m)
if @passwordChar!=""
chars = text.scan(/./m)
if @passwordChar != ""
chars.length.times { |i| chars[i] = @passwordChar }
end
return chars
end
def passwordChar=(value)
@passwordChar=value ? value : ""
@passwordChar = value || ""
end
def length
@@ -35,75 +31,70 @@ class CharacterEntryHelper
end
def canInsert?
chars=self.text.scan(/./m)
return false if @maxlength>=0 && chars.length>=@maxlength
chars = self.text.scan(/./m)
return false if @maxlength >= 0 && chars.length >= @maxlength
return true
end
def insert(ch)
chars=self.text.scan(/./m)
return false if @maxlength>=0 && chars.length>=@maxlength
chars.insert(@cursor,ch)
@text=""
for ch in chars
@text+=ch if ch
end
@cursor+=1
chars = self.text.scan(/./m)
return false if @maxlength >= 0 && chars.length >= @maxlength
chars.insert(@cursor, ch)
@text = ""
chars.each { |char| @text += char if char }
@cursor += 1
return true
end
def canDelete?
chars=self.text.scan(/./m)
return false if chars.length<=0 || @cursor<=0
chars = self.text.scan(/./m)
return false if chars.length <= 0 || @cursor <= 0
return true
end
def delete
chars=self.text.scan(/./m)
return false if chars.length<=0 || @cursor<=0
chars.delete_at(@cursor-1)
@text=""
for ch in chars
@text+=ch if ch
chars = self.text.scan(/./m)
return false if chars.length <= 0 || @cursor <= 0
chars.delete_at(@cursor - 1)
@text = ""
chars.each do |ch|
@text += ch if ch
end
@cursor-=1
@cursor -= 1
return true
end
#-----------------------------------------------------------------------------
private
def ensure
return if @maxlength<0
chars=self.text.scan(/./m)
if chars.length>@maxlength && @maxlength>=0
chars=chars[0,@maxlength]
end
@text=""
for ch in chars
@text+=ch if ch
return if @maxlength < 0
chars = self.text.scan(/./m)
chars = chars[0, @maxlength] if chars.length > @maxlength && @maxlength >= 0
@text = ""
chars.each do |ch|
@text += ch if ch
end
end
end
#===============================================================================
#
#===============================================================================
class Window_TextEntry < SpriteWindow_Base
def initialize(text,x,y,width,height,heading=nil,usedarkercolor=false)
super(x,y,width,height)
colors=getDefaultTextColors(self.windowskin)
@baseColor=colors[0]
@shadowColor=colors[1]
def initialize(text, x, y, width, height, heading = nil, usedarkercolor = false)
super(x, y, width, height)
@baseColor, @shadowColor = getDefaultTextColors(self.windowskin)
if usedarkercolor
@baseColor=Color.new(16,24,32)
@shadowColor=Color.new(168,184,184)
@baseColor = Color.new(16, 24, 32)
@shadowColor = Color.new(168, 184, 184)
end
@helper=CharacterEntryHelper.new(text)
@heading=heading
self.active=true
@frame=0
@helper = CharacterEntryHelper.new(text)
@heading = heading
@cursor_timer_start = System.uptime
@cursor_shown = true
self.active = true
refresh
end
@@ -120,23 +111,24 @@ class Window_TextEntry < SpriteWindow_Base
end
def text=(value)
@helper.text=value
@helper.text = value
self.refresh
end
def passwordChar=(value)
@helper.passwordChar=value
@helper.passwordChar = value
refresh
end
def maxlength=(value)
@helper.maxlength=value
@helper.maxlength = value
self.refresh
end
def insert(ch)
if @helper.insert(ch)
@frame=0
@cursor_timer_start = System.uptime
@cursor_shown = true
self.refresh
return true
end
@@ -145,7 +137,8 @@ class Window_TextEntry < SpriteWindow_Base
def delete
if @helper.delete
@frame=0
@cursor_timer_start = System.uptime
@cursor_shown = true
self.refresh
return true
end
@@ -153,21 +146,25 @@ class Window_TextEntry < SpriteWindow_Base
end
def update
@frame += 1
@frame %= 20
self.refresh if (@frame%10)==0
cursor_to_show = ((System.uptime - @cursor_timer_start) / 0.35).to_i.even?
if cursor_to_show != @cursor_shown
@cursor_shown = cursor_to_show
refresh
end
return if !self.active
# Moving cursor
if Input.repeat?(Input::LEFT) && Input.press?(Input::ACTION)
if @helper.cursor > 0
@helper.cursor -= 1
@frame = 0
@cursor_timer_start = System.uptime
@cursor_shown = true
self.refresh
end
elsif Input.repeat?(Input::RIGHT) && Input.press?(Input::ACTION)
if @helper.cursor < self.text.scan(/./m).length
@helper.cursor += 1
@frame = 0
@cursor_timer_start = System.uptime
@cursor_shown = true
self.refresh
end
elsif Input.repeat?(Input::BACK) # Backspace
@@ -176,79 +173,81 @@ class Window_TextEntry < SpriteWindow_Base
end
def refresh
self.contents=pbDoEnsureBitmap(self.contents,self.width-self.borderX,
self.height-self.borderY)
bitmap=self.contents
self.contents = pbDoEnsureBitmap(self.contents, self.width - self.borderX,
self.height - self.borderY)
bitmap = self.contents
bitmap.clear
x=0
y=0
x = 0
y = 0
if @heading
textwidth=bitmap.text_size(@heading).width
pbDrawShadowText(bitmap,x,y, textwidth+4, 32, @heading,@baseColor,@shadowColor)
y+=32
textwidth = bitmap.text_size(@heading).width
pbDrawShadowText(bitmap, x, y, textwidth + 4, 32, @heading, @baseColor, @shadowColor)
y += 32
end
x+=4
width=self.width-self.borderX
cursorcolor=Color.new(16,24,32)
textscan=self.text.scan(/./m)
scanlength=textscan.length
@helper.cursor=scanlength if @helper.cursor>scanlength
@helper.cursor=0 if @helper.cursor<0
startpos=@helper.cursor
fromcursor=0
while (startpos>0)
c=(@helper.passwordChar!="") ? @helper.passwordChar : textscan[startpos-1]
fromcursor+=bitmap.text_size(c).width
break if fromcursor>width-4
startpos-=1
x += 4
width = self.width - self.borderX
cursorcolor = Color.new(16, 24, 32)
textscan = self.text.scan(/./m)
scanlength = textscan.length
@helper.cursor = scanlength if @helper.cursor > scanlength
@helper.cursor = 0 if @helper.cursor < 0
startpos = @helper.cursor
fromcursor = 0
while startpos > 0
c = (@helper.passwordChar != "") ? @helper.passwordChar : textscan[startpos - 1]
fromcursor += bitmap.text_size(c).width
break if fromcursor > width - 4
startpos -= 1
end
for i in startpos...scanlength
c=(@helper.passwordChar!="") ? @helper.passwordChar : textscan[i]
textwidth=bitmap.text_size(c).width
next if c=="\n"
(startpos...scanlength).each do |i|
c = (@helper.passwordChar != "") ? @helper.passwordChar : textscan[i]
textwidth = bitmap.text_size(c).width
next if c == "\n"
# Draw text
pbDrawShadowText(bitmap,x,y, textwidth+4, 32, c,@baseColor,@shadowColor)
pbDrawShadowText(bitmap, x, y, textwidth + 4, 32, c, @baseColor, @shadowColor)
# Draw cursor if necessary
if ((@frame/10)&1) == 0 && i==@helper.cursor
bitmap.fill_rect(x,y+4,2,24,cursorcolor)
if i == @helper.cursor && @cursor_shown
bitmap.fill_rect(x, y + 4, 2, 24, cursorcolor)
end
# Add x to drawn text width
x += textwidth
end
if ((@frame/10)&1) == 0 && textscan.length==@helper.cursor
bitmap.fill_rect(x,y+4,2,24,cursorcolor)
if textscan.length == @helper.cursor && @cursor_shown
bitmap.fill_rect(x, y + 4, 2, 24, cursorcolor)
end
end
end
#===============================================================================
#
#===============================================================================
class Window_TextEntry_Keyboard < Window_TextEntry
def update
@frame+=1
@frame%=20
self.refresh if ((@frame%10)==0)
cursor_to_show = ((System.uptime - @cursor_timer_start) / 0.35).to_i.even?
if cursor_to_show != @cursor_shown
@cursor_shown = cursor_to_show
refresh
end
return if !self.active
# Moving cursor
if Input.triggerex?(:LEFT) || Input.repeatex?(:LEFT)
if @helper.cursor > 0
@helper.cursor-=1
@frame=0
@helper.cursor -= 1
@cursor_timer_start = System.uptime
@cursor_shown = true
self.refresh
end
return
elsif Input.triggerex?(:RIGHT) || Input.repeatex?(:RIGHT)
if @helper.cursor < self.text.scan(/./m).length
@helper.cursor+=1
@frame=0
@helper.cursor += 1
@cursor_timer_start = System.uptime
@cursor_shown = true
self.refresh
end
return
elsif Input.triggerex?(:BACKSPACE) || Input.repeatex?(:BACKSPACE)
self.delete if @helper.cursor>0
self.delete if @helper.cursor > 0
return
elsif Input.triggerex?(:RETURN) || Input.triggerex?(:ESCAPE)
return
@@ -257,23 +256,20 @@ class Window_TextEntry_Keyboard < Window_TextEntry
end
end
#===============================================================================
#
#===============================================================================
class Window_MultilineTextEntry < SpriteWindow_Base
def initialize(text,x,y,width,height)
super(x,y,width,height)
colors=getDefaultTextColors(self.windowskin)
@baseColor=colors[0]
@shadowColor=colors[1]
@helper=CharacterEntryHelper.new(text)
@firstline=0
@cursorLine=0
@cursorColumn=0
@frame=0
self.active=true
def initialize(text, x, y, width, height)
super(x, y, width, height)
@baseColor, @shadowColor = getDefaultTextColors(self.windowskin)
@helper = CharacterEntryHelper.new(text)
@firstline = 0
@cursorLine = 0
@cursorColumn = 0
@cursor_timer_start = System.uptime
@cursor_shown = true
self.active = true
refresh
end
@@ -281,12 +277,12 @@ class Window_MultilineTextEntry < SpriteWindow_Base
attr_reader :shadowColor
def baseColor=(value)
@baseColor=value
@baseColor = value
refresh
end
def shadowColor=(value)
@shadowColor=value
@shadowColor = value
refresh
end
@@ -299,23 +295,24 @@ class Window_MultilineTextEntry < SpriteWindow_Base
end
def text=(value)
@helper.text=value
@textchars=nil
@helper.text = value
@textchars = nil
self.refresh
end
def maxlength=(value)
@helper.maxlength=value
@textchars=nil
@helper.maxlength = value
@textchars = nil
self.refresh
end
def insert(ch)
@helper.cursor=getPosFromLineAndColumn(@cursorLine,@cursorColumn)
@helper.cursor = getPosFromLineAndColumn(@cursorLine, @cursorColumn)
if @helper.insert(ch)
@frame=0
@textchars=nil
moveCursor(0,1)
@cursor_timer_start = System.uptime
@cursor_shown = true
@textchars = nil
moveCursor(0, 1)
self.refresh
return true
end
@@ -323,11 +320,12 @@ class Window_MultilineTextEntry < SpriteWindow_Base
end
def delete
@helper.cursor=getPosFromLineAndColumn(@cursorLine,@cursorColumn)
@helper.cursor = getPosFromLineAndColumn(@cursorLine, @cursorColumn)
if @helper.delete
@frame=0
moveCursor(0,-1) # use old textchars
@textchars=nil
@cursor_timer_start = System.uptime
@cursor_shown = true
moveCursor(0, -1) # use old textchars
@textchars = nil
self.refresh
return true
end
@@ -336,163 +334,155 @@ class Window_MultilineTextEntry < SpriteWindow_Base
def getTextChars
if !@textchars
@textchars=getLineBrokenText(self.contents,@helper.text,
self.contents.width,nil)
@textchars = getLineBrokenText(self.contents, @helper.text,
self.contents.width, nil)
end
return @textchars
end
def getTotalLines
textchars=getTextChars
return 1 if textchars.length==0
tchar=textchars[textchars.length-1]
return tchar[5]+1
textchars = getTextChars
return 1 if textchars.length == 0
tchar = textchars[textchars.length - 1]
return tchar[5] + 1
end
def getLineY(line)
textchars=getTextChars
return 0 if textchars.length==0
totallines=getTotalLines()
line=0 if line<0
line=totallines-1 if line>=totallines
maximumY=0
for i in 0...textchars.length
thisline=textchars[i][5]
y=textchars[i][2]
return y if thisline==line
maximumY=y if maximumY<y
textchars = getTextChars
return 0 if textchars.length == 0
totallines = getTotalLines
line = 0 if line < 0
line = totallines - 1 if line >= totallines
maximumY = 0
textchars.each do |text|
thisline = text[5]
y = text[2]
return y if thisline == line
maximumY = y if maximumY < y
end
return maximumY
end
def getColumnsInLine(line)
textchars=getTextChars
return 0 if textchars.length==0
totallines=getTotalLines()
line=0 if line<0
line=totallines-1 if line>=totallines
endpos=0
for i in 0...textchars.length
thisline=textchars[i][5]
thislength=textchars[i][8]
endpos+=thislength if thisline==line
textchars = getTextChars
return 0 if textchars.length == 0
totallines = getTotalLines
line = 0 if line < 0
line = totallines - 1 if line >= totallines
endpos = 0
textchars.each do |text|
thisline = text[5]
thislength = text[8]
endpos += thislength if thisline == line
end
return endpos
end
def getPosFromLineAndColumn(line,column)
textchars=getTextChars
return 0 if textchars.length==0
totallines=getTotalLines()
line=0 if line<0
line=totallines-1 if line>=totallines
endpos=0
for i in 0...textchars.length
thisline=textchars[i][5]
thispos=textchars[i][6]
thiscolumn=textchars[i][7]
thislength=textchars[i][8]
if thisline==line
endpos=thispos+thislength
# echoln [endpos,thispos+(column-thiscolumn),textchars[i]]
if column>=thiscolumn && column<=thiscolumn+thislength && thislength>0
return thispos+(column-thiscolumn)
end
end
def getPosFromLineAndColumn(line, column)
textchars = getTextChars
return 0 if textchars.length == 0
totallines = getTotalLines
line = 0 if line < 0
line = totallines - 1 if line >= totallines
endpos = 0
textchars.each do |text|
thisline = text[5]
thispos = text[6]
thiscolumn = text[7]
thislength = text[8]
next if thisline != line
endpos = thispos + thislength
next if column < thiscolumn || column > thiscolumn + thislength || thislength == 0
return thispos + column - thiscolumn
end
# if endpos==0
# echoln [totallines,line,column]
# echoln textchars
# end
# echoln "endpos=#{endpos}"
return endpos
end
def getLastVisibleLine
getTextChars()
textheight=[1,self.contents.text_size("X").height].max
lastVisible=@firstline+((self.height-self.borderY)/textheight)-1
getTextChars
textheight = [1, self.contents.text_size("X").height].max
lastVisible = @firstline + ((self.height - self.borderY) / textheight) - 1
return lastVisible
end
def updateCursorPos(doRefresh)
# Calculate new cursor position
@helper.cursor=getPosFromLineAndColumn(@cursorLine,@cursorColumn)
@helper.cursor = getPosFromLineAndColumn(@cursorLine, @cursorColumn)
if doRefresh
@frame=0
@cursor_timer_start = System.uptime
@cursor_shown = true
self.refresh
end
@firstline=@cursorLine if @cursorLine<@firstline
lastVisible=getLastVisibleLine()
@firstline+=(@cursorLine-lastVisible) if @cursorLine>lastVisible
@firstline = @cursorLine if @cursorLine < @firstline
lastVisible = getLastVisibleLine
@firstline += (@cursorLine - lastVisible) if @cursorLine > lastVisible
end
def moveCursor(lineOffset, columnOffset)
# Move column offset first, then lines (since column offset
# can affect line offset)
# echoln ["beforemoving",@cursorLine,@cursorColumn]
totalColumns=getColumnsInLine(@cursorLine) # check current line
totalLines=getTotalLines()
oldCursorLine=@cursorLine
oldCursorColumn=@cursorColumn
@cursorColumn+=columnOffset
if @cursorColumn<0 && @cursorLine>0
totalColumns = getColumnsInLine(@cursorLine) # check current line
totalLines = getTotalLines
oldCursorLine = @cursorLine
oldCursorColumn = @cursorColumn
@cursorColumn += columnOffset
if @cursorColumn < 0 && @cursorLine > 0
# Will happen if cursor is moved left from the beginning of a line
@cursorLine-=1
@cursorColumn=getColumnsInLine(@cursorLine)
elsif @cursorColumn>totalColumns && @cursorLine<totalLines-1
@cursorLine -= 1
@cursorColumn = getColumnsInLine(@cursorLine)
elsif @cursorColumn > totalColumns && @cursorLine < totalLines - 1
# Will happen if cursor is moved right from the end of a line
@cursorLine+=1
@cursorColumn=0
@cursorLine += 1
@cursorColumn = 0
end
# Ensure column bounds
totalColumns=getColumnsInLine(@cursorLine)
@cursorColumn=totalColumns if @cursorColumn>totalColumns
@cursorColumn=0 if @cursorColumn<0 # totalColumns can be 0
totalColumns = getColumnsInLine(@cursorLine)
@cursorColumn = totalColumns if @cursorColumn > totalColumns
@cursorColumn = 0 if @cursorColumn < 0 # totalColumns can be 0
# Move line offset
@cursorLine+=lineOffset
@cursorLine=0 if @cursorLine<0
@cursorLine=totalLines-1 if @cursorLine>=totalLines
@cursorLine += lineOffset
@cursorLine = 0 if @cursorLine < 0
@cursorLine = totalLines - 1 if @cursorLine >= totalLines
# Ensure column bounds again
totalColumns=getColumnsInLine(@cursorLine)
@cursorColumn=totalColumns if @cursorColumn>totalColumns
@cursorColumn=0 if @cursorColumn<0 # totalColumns can be 0
updateCursorPos(
oldCursorLine!=@cursorLine ||
oldCursorColumn!=@cursorColumn
)
totalColumns = getColumnsInLine(@cursorLine)
@cursorColumn = totalColumns if @cursorColumn > totalColumns
@cursorColumn = 0 if @cursorColumn < 0 # totalColumns can be 0
updateCursorPos(oldCursorLine != @cursorLine || oldCursorColumn != @cursorColumn)
# echoln ["aftermoving",@cursorLine,@cursorColumn]
end
def update
@frame+=1
@frame%=20
self.refresh if ((@frame%10)==0)
cursor_to_show = ((System.uptime - @cursor_timer_start) / 0.35).to_i.even?
if cursor_to_show != @cursor_shown
@cursor_shown = cursor_to_show
refresh
end
return if !self.active
# Moving cursor
if Input.triggerex?(:UP) || Input.repeatex?(:UP)
moveCursor(-1,0)
moveCursor(-1, 0)
return
elsif Input.triggerex?(:DOWN) || Input.repeatex?(:DOWN)
moveCursor(1,0)
moveCursor(1, 0)
return
elsif Input.triggerex?(:LEFT) || Input.repeatex?(:LEFT)
moveCursor(0,-1)
moveCursor(0, -1)
return
elsif Input.triggerex?(:RIGHT) || Input.repeatex?(:RIGHT)
moveCursor(0,1)
moveCursor(0, 1)
return
end
if Input.press?(Input::CTRL) && Input.triggerex?(:HOME)
# Move cursor to beginning
@cursorLine=0
@cursorColumn=0
@cursorLine = 0
@cursorColumn = 0
updateCursorPos(true)
return
elsif Input.press?(Input::CTRL) && Input.triggerex?(:END)
# Move cursor to end
@cursorLine=getTotalLines()-1
@cursorColumn=getColumnsInLine(@cursorLine)
@cursorLine = getTotalLines - 1
@cursorColumn = getColumnsInLine(@cursorLine)
updateCursorPos(true)
return
elsif Input.triggerex?(:RETURN) || Input.repeatex?(:RETURN)
@@ -502,63 +492,61 @@ class Window_MultilineTextEntry < SpriteWindow_Base
self.delete
return
end
Input.gets.each_char{|c|insert(c)}
Input.gets.each_char { |c| insert(c) }
end
def refresh
newContents=pbDoEnsureBitmap(self.contents,self.width-self.borderX,
self.height-self.borderY)
@textchars=nil if self.contents!=newContents
self.contents=newContents
bitmap=self.contents
newContents = pbDoEnsureBitmap(self.contents, self.width - self.borderX,
self.height - self.borderY)
@textchars = nil if self.contents != newContents
self.contents = newContents
bitmap = self.contents
bitmap.clear
getTextChars
height=self.height-self.borderY
cursorcolor=Color.new(0,0,0)
textchars=getTextChars()
startY=getLineY(@firstline)
for i in 0...textchars.length
thisline=textchars[i][5]
thiscolumn=textchars[i][7]
thislength=textchars[i][8]
textY=textchars[i][2]-startY
height = self.height - self.borderY
cursorcolor = Color.black
textchars = getTextChars
startY = getLineY(@firstline)
textchars.each do |text|
thisline = text[5]
thislength = text[8]
textY = text[2] - startY
# Don't draw lines before the first or zero-length segments
next if thisline<@firstline || thislength==0
next if thisline < @firstline || thislength == 0
# Don't draw lines beyond the window's height
break if textY >= height
c=textchars[i][0]
c = text[0]
# Don't draw spaces
next if c==" "
textwidth=textchars[i][3]+4 # add 4 to prevent draw_text from stretching text
textheight=textchars[i][4]
next if c == " "
textwidth = text[3] + 4 # add 4 to prevent draw_text from stretching text
textheight = text[4]
# Draw text
pbDrawShadowText(bitmap, textchars[i][1], textY, textwidth, textheight, c, @baseColor, @shadowColor)
pbDrawShadowText(bitmap, text[1], textY, textwidth, textheight, c, @baseColor, @shadowColor)
end
# Draw cursor
if ((@frame/10)&1) == 0
textheight=bitmap.text_size("X").height
cursorY=(textheight*@cursorLine)-startY
cursorX=0
for i in 0...textchars.length
thisline=textchars[i][5]
thiscolumn=textchars[i][7]
thislength=textchars[i][8]
if thisline==@cursorLine && @cursorColumn>=thiscolumn &&
@cursorColumn<=thiscolumn+thislength
cursorY=textchars[i][2]-startY
cursorX=textchars[i][1]
textheight=textchars[i][4]
posToCursor=@cursorColumn-thiscolumn
if posToCursor>=0
partialString=textchars[i][0].scan(/./m)[0,posToCursor].join("")
cursorX+=bitmap.text_size(partialString).width
end
break
if @cursor_shown
textheight = bitmap.text_size("X").height
cursorY = (textheight * @cursorLine) - startY
cursorX = 0
textchars.each do |text|
thisline = text[5]
thiscolumn = text[7]
thislength = text[8]
next if thisline != @cursorLine || @cursorColumn < thiscolumn ||
@cursorColumn > thiscolumn + thislength
cursorY = text[2] - startY
cursorX = text[1]
textheight = text[4]
posToCursor = @cursorColumn - thiscolumn
if posToCursor >= 0
partialString = text[0].scan(/./m)[0, posToCursor].join
cursorX += bitmap.text_size(partialString).width
end
break
end
cursorY+=4
cursorHeight=[4,textheight-4,bitmap.text_size("X").height-4].max
bitmap.fill_rect(cursorX,cursorY,2,cursorHeight,cursorcolor)
cursorY += 4
cursorHeight = [4, textheight - 4, bitmap.text_size("X").height - 4].max
bitmap.fill_rect(cursorX, cursorY, 2, cursorHeight, cursorcolor)
end
end
end

View File

@@ -1,25 +1,9 @@
#####################################
# Needed because RGSS doesn't call at_exit procs on exit
# Exit is not called when game is reset (using F12)
$AtExitProcs=[] if !$AtExitProcs
def exit(code=0)
for p in $AtExitProcs
p.call
end
raise SystemExit.new(code)
end
def at_exit(&block)
$AtExitProcs.push(Proc.new(&block))
end
#===============================================================================
# Methods that determine the duration of an audio file.
#===============================================================================
def getOggPage(file)
fgetdw = proc { |file|
(file.eof? ? 0 : (file.read(4).unpack("V")[0] || 0))
fgetdw = proc { |f|
(f.eof? ? 0 : (f.read(4).unpack("V")[0] || 0))
}
dw = fgetdw.call(file)
return nil if dw != 0x5367674F
@@ -35,8 +19,8 @@ end
# internal function
def oggfiletime(file)
fgetdw = proc { |file|
(file.eof? ? 0 : (file.read(4).unpack("V")[0] || 0))
fgetdw = proc { |f|
(f.eof? ? 0 : (f.read(4).unpack("V")[0] || 0))
}
pages = []
page = nil
@@ -51,8 +35,8 @@ def oggfiletime(file)
i = -1
pcmlengths = []
rates = []
for page in pages
header = page[0]
pages.each do |pg|
header = pg[0]
serial = header[10, 4].unpack("V")
frame = header[2, 8].unpack("C*")
frameno = frame[7]
@@ -65,7 +49,7 @@ def oggfiletime(file)
frameno = (frameno << 8) | frame[0]
if serial != curserial
curserial = serial
file.pos = page[1]
file.pos = pg[1]
packtype = (file.read(1)[0].ord rescue 0)
string = file.read(6)
return -1 if string != "vorbis"
@@ -78,28 +62,26 @@ def oggfiletime(file)
pcmlengths[i] = frameno
end
ret = 0.0
for i in 0...pcmlengths.length
ret += pcmlengths[i].to_f / rates[i].to_f
end
pcmlengths.each_with_index { |length, j| ret += length.to_f / rates[j] }
return ret * 256.0
end
# Gets the length of an audio file in seconds. Supports WAV, MP3, and OGG files.
def getPlayTime(filename)
if safeExists?(filename)
if FileTest.exist?(filename)
return [getPlayTime2(filename), 0].max
elsif safeExists?(filename + ".wav")
elsif FileTest.exist?(filename + ".wav")
return [getPlayTime2(filename + ".wav"), 0].max
elsif safeExists?(filename + ".mp3")
elsif FileTest.exist?(filename + ".mp3")
return [getPlayTime2(filename + ".mp3"), 0].max
elsif safeExists?(filename + ".ogg")
elsif FileTest.exist?(filename + ".ogg")
return [getPlayTime2(filename + ".ogg"), 0].max
end
return 0
end
def getPlayTime2(filename)
return -1 if !safeExists?(filename)
return -1 if !FileTest.exist?(filename)
time = -1
fgetdw = proc { |file|
(file.eof? ? 0 : (file.read(4).unpack("V")[0] || 0))
@@ -107,39 +89,40 @@ def getPlayTime2(filename)
fgetw = proc { |file|
(file.eof? ? 0 : (file.read(2).unpack("v")[0] || 0))
}
File.open(filename, "rb") { |file|
File.open(filename, "rb") do |file|
file.pos = 0
fdw = fgetdw.call(file)
if fdw == 0x46464952 # "RIFF"
case fdw
when 0x46464952 # "RIFF"
filesize = fgetdw.call(file)
wave = fgetdw.call(file)
return -1 if wave != 0x45564157 # "WAVE"
fmt = fgetdw.call(file)
return -1 if fmt != 0x20746d66 # "fmt "
fmtsize = fgetdw.call(file)
format = fgetw.call(file)
channels = fgetw.call(file)
rate = fgetdw.call(file)
fgetdw.call(file) # fmtsize
fgetw.call(file) # format
fgetw.call(file) # channels
fgetdw.call(file) # rate
bytessec = fgetdw.call(file)
return -1 if bytessec == 0
bytessample = fgetw.call(file)
bitssample = fgetw.call(file)
fgetw.call(file) # bytessample
fgetw.call(file) # bitssample
data = fgetdw.call(file)
return -1 if data != 0x61746164 # "data"
datasize = fgetdw.call(file)
time = (datasize*1.0)/bytessec
time = datasize.to_f / bytessec
return time
elsif fdw == 0x5367674F # "OggS"
when 0x5367674F # "OggS"
file.pos = 0
time = oggfiletime(file)
return time
end
file.pos = 0
# Find the length of an MP3 file
while true
loop do
rstr = ""
ateof = false
while !file.eof?
until file.eof?
if (file.read(1)[0] rescue 0) == 0xFF
begin
rstr = file.read(3)
@@ -152,20 +135,20 @@ def getPlayTime2(filename)
break if ateof || !rstr || rstr.length != 3
if rstr[0] == 0xFB
t = rstr[1] >> 4
next if t == 0 || t == 15
freqs = [44100, 22050, 11025, 48000]
next if [0, 15].include?(t)
freqs = [44_100, 22_050, 11_025, 48_000]
bitrates = [32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320]
bitrate = bitrates[t]
t = (rstr[1] >> 2) & 3
freq = freqs[t]
t = (rstr[1] >> 1) & 1
filesize = FileTest.size(filename)
frameLength = ((144000 * bitrate) / freq) + t
frameLength = ((144_000 * bitrate) / freq) + t
numFrames = filesize / (frameLength + 4)
time = (numFrames * 1152.0 / freq)
break
end
end
}
end
return time
end

View File

@@ -1,15 +1,18 @@
#===============================================================================
#
#===============================================================================
def pbStringToAudioFile(str)
if str[/^(.*)\:\s*(\d+)\s*\:\s*(\d+)\s*$/] # Of the format "XXX: ###: ###"
file = $1
volume = $2.to_i
pitch = $3.to_i
return RPG::AudioFile.new(file,volume,pitch)
return RPG::AudioFile.new(file, volume, pitch)
elsif str[/^(.*)\:\s*(\d+)\s*$/] # Of the format "XXX: ###"
file = $1
volume = $2.to_i
return RPG::AudioFile.new(file,volume,100)
return RPG::AudioFile.new(file, volume, 100)
else
return RPG::AudioFile.new(str,100,100)
return RPG::AudioFile.new(str, 100, 100)
end
end
@@ -21,7 +24,7 @@ end
# filename:volume:pitch
# volume -- Volume of the file, up to 100
# pitch -- Pitch of the file, normally 100
def pbResolveAudioFile(str,volume=nil,pitch=nil)
def pbResolveAudioFile(str, volume = nil, pitch = nil)
if str.is_a?(String)
str = pbStringToAudioFile(str)
str.volume = volume || 100
@@ -29,8 +32,8 @@ def pbResolveAudioFile(str,volume=nil,pitch=nil)
end
if str.is_a?(RPG::AudioFile)
if volume || pitch
return RPG::AudioFile.new(str.name,volume || str.volume || 100 ,
pitch || str.pitch || 100)
return RPG::AudioFile.new(str.name, volume || str.volume || 100,
pitch || str.pitch || 100)
else
return str
end
@@ -38,8 +41,9 @@ def pbResolveAudioFile(str,volume=nil,pitch=nil)
return str
end
################################################################################
#===============================================================================
#
#===============================================================================
# Plays a BGM file.
# param -- Either a string showing the filename
# (relative to Audio/BGM/) or an RPG::AudioFile object.
@@ -49,47 +53,48 @@ end
# filename:volume:pitch
# volume -- Volume of the file, up to 100
# pitch -- Pitch of the file, normally 100
def pbBGMPlay(param,volume=nil,pitch=nil)
def pbBGMPlay(param, volume = nil, pitch = nil)
return if !param
param=pbResolveAudioFile(param,volume,pitch)
if param.name && param.name!=""
if $game_system && $game_system.respond_to?("bgm_play")
param = pbResolveAudioFile(param, volume, pitch)
if param.name && param.name != ""
if $game_system
$game_system.bgm_play(param)
return
elsif (RPG.const_defined?(:BGM) rescue false)
b=RPG::BGM.new(param.name,param.volume,param.pitch)
if b && b.respond_to?("play")
b = RPG::BGM.new(param.name, param.volume, param.pitch)
if b.respond_to?("play")
b.play
return
end
end
Audio.bgm_play(canonicalize("Audio/BGM/"+param.name),param.volume,param.pitch)
Audio.bgm_play(canonicalize("Audio/BGM/" + param.name), param.volume, param.pitch)
end
end
# Fades out or stops BGM playback. 'x' is the time in seconds to fade out.
def pbBGMFade(x=0.0); pbBGMStop(x);end
def pbBGMFade(x = 0.0); pbBGMStop(x); end
# Fades out or stops BGM playback. 'x' is the time in seconds to fade out.
def pbBGMStop(timeInSeconds=0.0)
if $game_system && timeInSeconds>0.0 && $game_system.respond_to?("bgm_fade")
def pbBGMStop(timeInSeconds = 0.0)
if $game_system && timeInSeconds > 0.0
$game_system.bgm_fade(timeInSeconds)
return
elsif $game_system && $game_system.respond_to?("bgm_stop")
elsif $game_system
$game_system.bgm_stop
return
elsif (RPG.const_defined?(:BGM) rescue false)
begin
(timeInSeconds>0.0) ? RPG::BGM.fade((timeInSeconds*1000).floor) : RPG::BGM.stop
(timeInSeconds > 0.0) ? RPG::BGM.fade((timeInSeconds * 1000).floor) : RPG::BGM.stop
return
rescue
end
end
(timeInSeconds>0.0) ? Audio.bgm_fade((timeInSeconds*1000).floor) : Audio.bgm_stop
(timeInSeconds > 0.0) ? Audio.bgm_fade((timeInSeconds * 1000).floor) : Audio.bgm_stop
end
################################################################################
#===============================================================================
#
#===============================================================================
# Plays an ME file.
# param -- Either a string showing the filename
# (relative to Audio/ME/) or an RPG::AudioFile object.
@@ -99,46 +104,48 @@ end
# filename:volume:pitch
# volume -- Volume of the file, up to 100
# pitch -- Pitch of the file, normally 100
def pbMEPlay(param,volume=nil,pitch=nil)
def pbMEPlay(param, volume = nil, pitch = nil)
return if !param
param=pbResolveAudioFile(param,volume,pitch)
if param.name && param.name!=""
if $game_system && $game_system.respond_to?("me_play")
param = pbResolveAudioFile(param, volume, pitch)
if param.name && param.name != ""
if $game_system
$game_system.me_play(param)
return
elsif (RPG.const_defined?(:ME) rescue false)
b=RPG::ME.new(param.name,param.volume,param.pitch)
if b && b.respond_to?("play")
b.play; return
b = RPG::ME.new(param.name, param.volume, param.pitch)
if b.respond_to?("play")
b.play
return
end
end
Audio.me_play(canonicalize("Audio/ME/"+param.name),param.volume,param.pitch)
Audio.me_play(canonicalize("Audio/ME/" + param.name), param.volume, param.pitch)
end
end
# Fades out or stops ME playback. 'x' is the time in seconds to fade out.
def pbMEFade(x=0.0); pbMEStop(x);end
def pbMEFade(x = 0.0); pbMEStop(x); end
# Fades out or stops ME playback. 'x' is the time in seconds to fade out.
def pbMEStop(timeInSeconds=0.0)
if $game_system && timeInSeconds>0.0 && $game_system.respond_to?("me_fade")
def pbMEStop(timeInSeconds = 0.0)
if $game_system && timeInSeconds > 0.0 && $game_system.respond_to?("me_fade")
$game_system.me_fade(timeInSeconds)
return
elsif $game_system && $game_system.respond_to?("me_stop")
elsif $game_system.respond_to?("me_stop")
$game_system.me_stop(nil)
return
elsif (RPG.const_defined?(:ME) rescue false)
begin
(timeInSeconds>0.0) ? RPG::ME.fade((timeInSeconds*1000).floor) : RPG::ME.stop
(timeInSeconds > 0.0) ? RPG::ME.fade((timeInSeconds * 1000).floor) : RPG::ME.stop
return
rescue
end
end
(timeInSeconds>0.0) ? Audio.me_fade((timeInSeconds*1000).floor) : Audio.me_stop
(timeInSeconds > 0.0) ? Audio.me_fade((timeInSeconds * 1000).floor) : Audio.me_stop
end
################################################################################
#===============================================================================
#
#===============================================================================
# Plays a BGS file.
# param -- Either a string showing the filename
# (relative to Audio/BGS/) or an RPG::AudioFile object.
@@ -148,46 +155,48 @@ end
# filename:volume:pitch
# volume -- Volume of the file, up to 100
# pitch -- Pitch of the file, normally 100
def pbBGSPlay(param,volume=nil,pitch=nil)
def pbBGSPlay(param, volume = nil, pitch = nil)
return if !param
param=pbResolveAudioFile(param,volume,pitch)
if param.name && param.name!=""
if $game_system && $game_system.respond_to?("bgs_play")
param = pbResolveAudioFile(param, volume, pitch)
if param.name && param.name != ""
if $game_system
$game_system.bgs_play(param)
return
elsif (RPG.const_defined?(:BGS) rescue false)
b=RPG::BGS.new(param.name,param.volume,param.pitch)
if b && b.respond_to?("play")
b.play; return
b = RPG::BGS.new(param.name, param.volume, param.pitch)
if b.respond_to?("play")
b.play
return
end
end
Audio.bgs_play(canonicalize("Audio/BGS/"+param.name),param.volume,param.pitch)
Audio.bgs_play(canonicalize("Audio/BGS/" + param.name), param.volume, param.pitch)
end
end
# Fades out or stops BGS playback. 'x' is the time in seconds to fade out.
def pbBGSFade(x=0.0); pbBGSStop(x);end
def pbBGSFade(x = 0.0); pbBGSStop(x); end
# Fades out or stops BGS playback. 'x' is the time in seconds to fade out.
def pbBGSStop(timeInSeconds=0.0)
if $game_system && timeInSeconds>0.0 && $game_system.respond_to?("bgs_fade")
def pbBGSStop(timeInSeconds = 0.0)
if $game_system && timeInSeconds > 0.0
$game_system.bgs_fade(timeInSeconds)
return
elsif $game_system && $game_system.respond_to?("bgs_play")
elsif $game_system
$game_system.bgs_play(nil)
return
elsif (RPG.const_defined?(:BGS) rescue false)
begin
(timeInSeconds>0.0) ? RPG::BGS.fade((timeInSeconds*1000).floor) : RPG::BGS.stop
(timeInSeconds > 0.0) ? RPG::BGS.fade((timeInSeconds * 1000).floor) : RPG::BGS.stop
return
rescue
end
end
(timeInSeconds>0.0) ? Audio.bgs_fade((timeInSeconds*1000).floor) : Audio.bgs_stop
(timeInSeconds > 0.0) ? Audio.bgs_fade((timeInSeconds * 1000).floor) : Audio.bgs_stop
end
################################################################################
#===============================================================================
#
#===============================================================================
# Plays an SE file.
# param -- Either a string showing the filename
# (relative to Audio/SE/) or an RPG::AudioFile object.
@@ -197,30 +206,30 @@ end
# filename:volume:pitch
# volume -- Volume of the file, up to 100
# pitch -- Pitch of the file, normally 100
def pbSEPlay(param,volume=nil,pitch=nil)
def pbSEPlay(param, volume = nil, pitch = nil)
return if !param
param = pbResolveAudioFile(param,volume,pitch)
if param.name && param.name!=""
if $game_system && $game_system.respond_to?("se_play")
param = pbResolveAudioFile(param, volume, pitch)
if param.name && param.name != ""
if $game_system
$game_system.se_play(param)
return
end
if (RPG.const_defined?(:SE) rescue false)
b = RPG::SE.new(param.name,param.volume,param.pitch)
if b && b.respond_to?("play")
b = RPG::SE.new(param.name, param.volume, param.pitch)
if b.respond_to?("play")
b.play
return
end
end
Audio.se_play(canonicalize("Audio/SE/"+param.name),param.volume,param.pitch)
Audio.se_play(canonicalize("Audio/SE/" + param.name), param.volume, param.pitch)
end
end
# Stops SE playback.
def pbSEFade(x=0.0); pbSEStop(x);end
def pbSEFade(x = 0.0); pbSEStop(x); end
# Stops SE playback.
def pbSEStop(_timeInSeconds=0.0)
def pbSEStop(_timeInSeconds = 0.0)
if $game_system
$game_system.se_stop
elsif (RPG.const_defined?(:SE) rescue false)
@@ -230,63 +239,45 @@ def pbSEStop(_timeInSeconds=0.0)
end
end
################################################################################
# Plays a sound effect that plays when the player moves the cursor.
def pbPlayCursorSE
if $data_system && $data_system.respond_to?("cursor_se") &&
$data_system.cursor_se && $data_system.cursor_se.name!=""
if !nil_or_empty?($data_system&.cursor_se&.name)
pbSEPlay($data_system.cursor_se)
elsif $data_system && $data_system.respond_to?("sounds") &&
$data_system.sounds && $data_system.sounds[0] && $data_system.sounds[0].name!=""
pbSEPlay($data_system.sounds[0])
elsif FileTest.audio_exist?("Audio/SE/GUI sel cursor")
pbSEPlay("GUI sel cursor",80)
pbSEPlay("GUI sel cursor", 80)
end
end
# Plays a sound effect that plays when a decision is confirmed or a choice is made.
def pbPlayDecisionSE
if $data_system && $data_system.respond_to?("decision_se") &&
$data_system.decision_se && $data_system.decision_se.name!=""
if !nil_or_empty?($data_system&.decision_se&.name)
pbSEPlay($data_system.decision_se)
elsif $data_system && $data_system.respond_to?("sounds") &&
$data_system.sounds && $data_system.sounds[1] && $data_system.sounds[1].name!=""
pbSEPlay($data_system.sounds[1])
elsif FileTest.audio_exist?("Audio/SE/GUI sel decision")
pbSEPlay("GUI sel decision",80)
pbSEPlay("GUI sel decision", 80)
end
end
# Plays a sound effect that plays when a choice is canceled.
def pbPlayCancelSE
if $data_system && $data_system.respond_to?("cancel_se") &&
$data_system.cancel_se && $data_system.cancel_se.name!=""
if !nil_or_empty?($data_system&.cancel_se&.name)
pbSEPlay($data_system.cancel_se)
elsif $data_system && $data_system.respond_to?("sounds") &&
$data_system.sounds && $data_system.sounds[2] && $data_system.sounds[2].name!=""
pbSEPlay($data_system.sounds[2])
elsif FileTest.audio_exist?("Audio/SE/GUI sel cancel")
pbSEPlay("GUI sel cancel",80)
pbSEPlay("GUI sel cancel", 80)
end
end
# Plays a buzzer sound effect.
def pbPlayBuzzerSE
if $data_system && $data_system.respond_to?("buzzer_se") &&
$data_system.buzzer_se && $data_system.buzzer_se.name!=""
if !nil_or_empty?($data_system&.buzzer_se&.name)
pbSEPlay($data_system.buzzer_se)
elsif $data_system && $data_system.respond_to?("sounds") &&
$data_system.sounds && $data_system.sounds[3] && $data_system.sounds[3].name!=""
pbSEPlay($data_system.sounds[3])
elsif FileTest.audio_exist?("Audio/SE/GUI sel buzzer")
pbSEPlay("GUI sel buzzer",80)
pbSEPlay("GUI sel buzzer", 80)
end
end
# Plays a sound effect that plays when the player moves the cursor.
# Plays a sound effect that plays when the player closes a menu.
def pbPlayCloseMenuSE
if FileTest.audio_exist?("Audio/SE/GUI menu close")
pbSEPlay("GUI menu close",80)
pbSEPlay("GUI menu close", 80)
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,7 @@
class PictureSprite < SpriteWrapper
#===============================================================================
#
#===============================================================================
class PictureSprite < Sprite
def initialize(viewport, picture)
super(viewport)
@picture = picture
@@ -10,7 +13,7 @@ class PictureSprite < SpriteWrapper
end
def dispose
@pictureBitmap.dispose if @pictureBitmap
@pictureBitmap&.dispose
super
end
@@ -22,23 +25,23 @@ class PictureSprite < SpriteWrapper
def update
super
@pictureBitmap.update if @pictureBitmap
@pictureBitmap&.update
# If picture file name is different from current one
if @customBitmap && @picture.name==""
if @customBitmap && @picture.name == ""
self.bitmap = (@customBitmapIsBitmap) ? @customBitmap : @customBitmap.bitmap
elsif @picture_name != @picture.name || @picture.hue.to_i != @hue.to_i
elsif @picture_name != @picture.name || @picture.hue.to_i != @hue.to_i
# Remember file name to instance variables
@picture_name = @picture.name
@hue = @picture.hue.to_i
# If file name is not empty
if @picture_name == ""
@pictureBitmap.dispose if @pictureBitmap
@pictureBitmap&.dispose
@pictureBitmap = nil
self.visible = false
return
end
# Get picture graphic
@pictureBitmap.dispose if @pictureBitmap
@pictureBitmap&.dispose
@pictureBitmap = AnimatedBitmap.new(@picture_name, @hue)
self.bitmap = (@pictureBitmap) ? @pictureBitmap.bitmap : nil
elsif @picture_name == ""
@@ -46,28 +49,24 @@ class PictureSprite < SpriteWrapper
self.visible = false
return
end
setPictureSprite(self,@picture)
setPictureSprite(self, @picture)
end
end
def pbTextBitmap(text, maxwidth=Graphics.width)
tmp = Bitmap.new(maxwidth,Graphics.height)
def pbTextBitmap(text, maxwidth = Graphics.width)
tmp = Bitmap.new(maxwidth, Graphics.height)
pbSetSystemFont(tmp)
drawFormattedTextEx(tmp,0,0,maxwidth,text,Color.new(248,248,248),Color.new(168,184,184))
drawFormattedTextEx(tmp, 0, 4, maxwidth, text, Color.new(248, 248, 248), Color.new(168, 184, 184))
return tmp
end
#===============================================================================
# EventScene
#
#===============================================================================
class EventScene
attr_accessor :onCTrigger,:onBTrigger,:onUpdate
attr_accessor :onCTrigger, :onBTrigger, :onUpdate
def initialize(viewport=nil)
def initialize(viewport = nil)
@viewport = viewport
@onCTrigger = Event.new
@onBTrigger = Event.new
@@ -80,12 +79,8 @@ class EventScene
def dispose
return if disposed?
for sprite in @picturesprites
sprite.dispose
end
for sprite in @usersprites
sprite.dispose
end
@picturesprites.each { |sprite| sprite.dispose }
@usersprites.each { |sprite| sprite.dispose }
@onCTrigger.clear
@onBTrigger.clear
@onUpdate.clear
@@ -105,26 +100,26 @@ class EventScene
# EventScene doesn't take ownership of the passed-in bitmap
num = @pictures.length
picture = PictureEx.new(num)
picture.setXY(0,x,y)
picture.setVisible(0,true)
picture.setXY(0, x, y)
picture.setVisible(0, true)
@pictures[num] = picture
@picturesprites[num] = PictureSprite.new(@viewport,picture)
@picturesprites[num] = PictureSprite.new(@viewport, picture)
@picturesprites[num].setCustomBitmap(bitmap)
return picture
end
def addLabel(x, y, width, text)
addBitmap(x,y,pbTextBitmap(text,width))
addBitmap(x, y, pbTextBitmap(text, width))
end
def addImage(x, y, name)
num = @pictures.length
picture = PictureEx.new(num)
picture.name = name
picture.setXY(0,x,y)
picture.setVisible(0,true)
picture.setXY(0, x, y)
picture.setVisible(0, true)
@pictures[num] = picture
@picturesprites[num] = PictureSprite.new(@viewport,picture)
@picturesprites[num] = PictureSprite.new(@viewport, picture)
return picture
end
@@ -136,36 +131,38 @@ class EventScene
return @pictures[num]
end
def wait(frames)
frames.times { update }
# ticks is in 1/20ths of a second.
def wait(ticks)
return if ticks <= 0
timer_start = System.uptime
loop do
update
break if System.uptime - timer_start >= ticks / 20.0
end
end
def pictureWait(extraframes=0)
# extra_ticks is in 1/20ths of a second.
def pictureWait(extra_ticks = 0)
loop do
hasRunning = false
for pic in @pictures
hasRunning = true if pic.running?
end
@pictures.each { |pic| hasRunning = true if pic.running? }
break if !hasRunning
update
end
extraframes.times { update }
wait(extra_ticks)
end
def update
return if disposed?
Graphics.update
Input.update
for picture in @pictures
picture.update
end
for sprite in @picturesprites
sprite.update
end
for sprite in @usersprites
@pictures.each { |picture| picture.update }
@picturesprites.each { |sprite| sprite.update }
@usersprites.each do |sprite|
next if !sprite || sprite.disposed? || !sprite.is_a?(Sprite)
sprite.update
end
@usersprites.delete_if { |sprite| sprite.disposed? }
@onUpdate.trigger(self)
if Input.trigger?(Input::BACK)
@onBTrigger.trigger(self)
@@ -175,24 +172,21 @@ class EventScene
end
def main
while !disposed?
loop do
update
break if disposed?
end
end
end
#===============================================================================
#
#===============================================================================
def pbEventScreen(cls)
pbFadeOutIn {
viewport = Viewport.new(0,0,Graphics.width,Graphics.height)
pbFadeOutIn do
viewport = Viewport.new(0, 0, Graphics.width, Graphics.height)
viewport.z = 99999
PBDebug.logonerr {
cls.new(viewport).main
}
PBDebug.logonerr { cls.new(viewport).main }
viewport.dispose
}
end
end

View File

@@ -1,3 +1,6 @@
#===============================================================================
#
#===============================================================================
module GameData
#=============================================================================
# A mixin module for data classes which provides common class methods (called
@@ -6,6 +9,10 @@ module GameData
# For data that is known by a symbol or an ID number.
#=============================================================================
module ClassMethods
def schema
return self::SCHEMA
end
def register(hash)
self::DATA[hash[:id]] = self::DATA[hash[:id_number]] = self.new(hash)
end
@@ -26,9 +33,6 @@ module GameData
validate other => [Symbol, self, String, Integer]
return other if other.is_a?(self)
other = other.to_sym if other.is_a?(String)
# if other.is_a?(Integer)
# p "Please switch to symbols, thanks."
# end
raise "Unknown ID #{other}." unless self::DATA.has_key?(other)
return self::DATA[other]
end
@@ -40,9 +44,6 @@ module GameData
validate other => [Symbol, self, String, Integer]
return other if other.is_a?(self)
other = other.to_sym if other.is_a?(String)
# if other.is_a?(Integer)
# p "Please switch to symbols, thanks."
# end
return (self::DATA.has_key?(other)) ? self::DATA[other] : nil
end
@@ -54,8 +55,12 @@ module GameData
# Yields all data in order of their id_number.
def each
keys = self::DATA.keys.sort { |a, b| self::DATA[a].id_number <=> self::DATA[b].id_number }
keys.each { |key| yield self::DATA[key] if !key.is_a?(Integer) }
sorted_keys = self::DATA.keys.sort { |a, b| self::DATA[a].id_number <=> self::DATA[b].id_number }
sorted_keys.each { |key| yield self::DATA[key] if !key.is_a?(Integer) }
end
def count
return self::DATA.length / 2
end
def load
@@ -74,6 +79,10 @@ module GameData
# For data that is only known by a symbol.
#=============================================================================
module ClassMethodsSymbols
def schema
return self::SCHEMA
end
def register(hash)
self::DATA[hash[:id]] = self.new(hash)
end
@@ -114,12 +123,21 @@ module GameData
return self::DATA.keys
end
# Yields all data in alphabetical order.
# Yields all data in the order they were defined.
def each
self::DATA.each_value { |value| yield value }
end
# Yields all data in alphabetical order.
def each_alphabetically
keys = self::DATA.keys.sort { |a, b| self::DATA[a].real_name <=> self::DATA[b].real_name }
keys.each { |key| yield self::DATA[key] }
end
def count
return self::DATA.length
end
def load
const_set(:DATA, load_data("Data/#{self::DATA_FILENAME}"))
end
@@ -136,6 +154,10 @@ module GameData
# For data that is only known by an ID number.
#=============================================================================
module ClassMethodsIDNumbers
def schema
return self::SCHEMA
end
def register(hash)
self::DATA[hash[:id]] = self.new(hash)
end
@@ -171,12 +193,16 @@ module GameData
return self::DATA.keys
end
# Yields all data in numberical order.
# Yields all data in numerical order.
def each
keys = self::DATA.keys.sort
keys.each { |key| yield self::DATA[key] }
end
def count
return self::DATA.length
end
def load
const_set(:DATA, load_data("Data/#{self::DATA_FILENAME}"))
end
@@ -196,34 +222,65 @@ module GameData
# @return [Boolean] whether other represents the same thing as this thing
def ==(other)
return false if other.nil?
if other.is_a?(Symbol)
case other
when Symbol
return @id == other
elsif other.is_a?(self.class)
when self.class
return @id == other.id
elsif other.is_a?(String)
return @id_number == other.to_sym
elsif other.is_a?(Integer)
when String
return @id == other.to_sym
when Integer
return @id_number == other
end
return false
end
def get_property_for_PBS(key)
ret = nil
if self.class::SCHEMA.include?(key) && self.respond_to?(self.class::SCHEMA[key][0])
ret = self.send(self.class::SCHEMA[key][0])
ret = nil if ret == false || (ret.is_a?(Array) && ret.length == 0)
end
return ret
end
end
#=============================================================================
# A bulk loader method for all data stored in .dat files in the Data folder.
#=============================================================================
def self.load_all
Type.load
Ability.load
Move.load
Item.load
BerryPlant.load
Species.load
Ribbon.load
Encounter.load
TrainerType.load
Trainer.load
Metadata.load
MapMetadata.load
self.constants.each do |c|
next if !self.const_get(c).is_a?(Class)
self.const_get(c).load if self.const_get(c).const_defined?(:DATA_FILENAME)
end
end
def self.get_all_data_filenames
ret = []
self.constants.each do |c|
next if !self.const_get(c).is_a?(Class)
next if !self.const_get(c).const_defined?(:DATA_FILENAME)
if self.const_get(c).const_defined?(:OPTIONAL) && self.const_get(c)::OPTIONAL
ret.push([self.const_get(c)::DATA_FILENAME, false])
else
ret.push([self.const_get(c)::DATA_FILENAME, true])
end
end
return ret
end
def self.get_all_pbs_base_filenames
ret = {}
self.constants.each do |c|
next if !self.const_get(c).is_a?(Class)
ret[c] = self.const_get(c)::PBS_BASE_FILENAME if self.const_get(c).const_defined?(:PBS_BASE_FILENAME)
next if !ret[c].is_a?(Array)
ret[c].length.times do |i|
next if i == 0
ret[(c.to_s + i.to_s).to_sym] = ret[c][i] # :Species1 => "pokemon_forms"
end
ret[c] = ret[c][0] # :Species => "pokemon"
end
return ret
end
end

View File

@@ -1,3 +1,6 @@
#===============================================================================
#
#===============================================================================
module GameData
class GrowthRate
attr_reader :id
@@ -24,6 +27,8 @@ module GameData
return Settings::MAXIMUM_LEVEL
end
#---------------------------------------------------------------------------
def initialize(hash)
@id = hash[:id]
@real_name = hash[:name] || "Unnamed"
@@ -64,7 +69,7 @@ module GameData
return ArgumentError.new("Exp amount #{level} is invalid.") if !exp || exp < 0
max = GrowthRate.max_level
return max if exp >= maximum_exp
for level in 1..max
(1..max).each do |level|
return level - 1 if exp < minimum_exp_for_level(level)
end
return max
@@ -72,46 +77,47 @@ module GameData
end
end
#===============================================================================
#
#===============================================================================
GameData::GrowthRate.register({
:id => :Medium, # Also known as Medium Fast
:name => _INTL("Medium"),
:exp_values => [-1,
0, 8, 27, 64, 125, 216, 343, 512, 729, 1000,
1331, 1728, 2197, 2744, 3375, 4096, 4913, 5832, 6859, 8000,
9261, 10648, 12167, 13824, 15625, 17576, 19683, 21952, 24389, 27000,
29791, 32768, 35937, 39304, 42875, 46656, 50653, 54872, 59319, 64000,
68921, 74088, 79507, 85184, 91125, 97336, 103823, 110592, 117649, 125000,
132651, 140608, 148877, 157464, 166375, 175616, 185193, 195112, 205379, 216000,
226981, 238328, 250047, 262144, 274625, 287496, 300763, 314432, 328509, 343000,
357911, 373248, 389017, 405224, 421875, 438976, 456533, 474552, 493039, 512000,
531441, 551368, 571787, 592704, 614125, 636056, 658503, 681472, 704969, 729000,
753571, 778688, 804357, 830584, 857375, 884736, 912673, 941192, 970299, 1000000],
:exp_formula => proc { |level| next level ** 3 }
0, 8, 27, 64, 125, 216, 343, 512, 729, 1000,
1331, 1728, 2197, 2744, 3375, 4096, 4913, 5832, 6859, 8000,
9261, 10648, 12167, 13824, 15625, 17576, 19683, 21952, 24389, 27000,
29791, 32768, 35937, 39304, 42875, 46656, 50653, 54872, 59319, 64000,
68921, 74088, 79507, 85184, 91125, 97336, 103823, 110592, 117649, 125000,
132651, 140608, 148877, 157464, 166375, 175616, 185193, 195112, 205379, 216000,
226981, 238328, 250047, 262144, 274625, 287496, 300763, 314432, 328509, 343000,
357911, 373248, 389017, 405224, 421875, 438976, 456533, 474552, 493039, 512000,
531441, 551368, 571787, 592704, 614125, 636056, 658503, 681472, 704969, 729000,
753571, 778688, 804357, 830584, 857375, 884736, 912673, 941192, 970299, 1000000],
:exp_formula => proc { |level| next level**3 }
})
# Erratic (600000):
# For levels 0-50: n**3 * (100 - n) / 50
# For levels 51-68: n**3 * (150 - n) / 100
# For levels 69-98: n**3 * 1.274 - (n / 150) - p(n mod 3)
# where p(x) = array(0.000, 0.008, 0.014)[x]
# For levels 69-98: n**3 * ((1911 - (10 * level)) / 3).floor / 500
# For levels 99-100: n**3 * (160 - n) / 100
GameData::GrowthRate.register({
:id => :Erratic,
:name => _INTL("Erratic"),
:exp_values => [-1,
0, 15, 52, 122, 237, 406, 637, 942, 1326, 1800,
2369, 3041, 3822, 4719, 5737, 6881, 8155, 9564, 11111, 12800,
14632, 16610, 18737, 21012, 23437, 26012, 28737, 31610, 34632, 37800,
41111, 44564, 48155, 51881, 55737, 59719, 63822, 68041, 72369, 76800,
81326, 85942, 90637, 95406, 100237, 105122, 110052, 115015, 120001, 125000,
131324, 137795, 144410, 151165, 158056, 165079, 172229, 179503, 186894, 194400,
202013, 209728, 217540, 225443, 233431, 241496, 249633, 257834, 267406, 276458,
286328, 296358, 305767, 316074, 326531, 336255, 346965, 357812, 367807, 378880,
390077, 400293, 411686, 423190, 433572, 445239, 457001, 467489, 479378, 491346,
501878, 513934, 526049, 536557, 548720, 560922, 571333, 583539, 591882, 600000],
:exp_formula => proc { |level| next (level ** 4) * 3 / 500 }
0, 15, 52, 122, 237, 406, 637, 942, 1326, 1800,
2369, 3041, 3822, 4719, 5737, 6881, 8155, 9564, 11111, 12800,
14632, 16610, 18737, 21012, 23437, 26012, 28737, 31610, 34632, 37800,
41111, 44564, 48155, 51881, 55737, 59719, 63822, 68041, 72369, 76800,
81326, 85942, 90637, 95406, 100237, 105122, 110052, 115015, 120001, 125000,
131324, 137795, 144410, 151165, 158056, 165079, 172229, 179503, 186894, 194400,
202013, 209728, 217540, 225443, 233431, 241496, 249633, 257834, 267406, 276458,
286328, 296358, 305767, 316074, 326531, 336255, 346965, 357812, 367807, 378880,
390077, 400293, 411686, 423190, 433572, 445239, 457001, 467489, 479378, 491346,
501878, 513934, 526049, 536557, 548720, 560922, 571333, 583539, 591882, 600000],
:exp_formula => proc { |level| next ((level**4) + ((level**3) * 2000)) / 3500 }
})
# Fluctuating (1640000):
@@ -122,19 +128,18 @@ GameData::GrowthRate.register({
:id => :Fluctuating,
:name => _INTL("Fluctuating"),
:exp_values => [-1,
0, 4, 13, 32, 65, 112, 178, 276, 393, 540,
745, 967, 1230, 1591, 1957, 2457, 3046, 3732, 4526, 5440,
6482, 7666, 9003, 10506, 12187, 14060, 16140, 18439, 20974, 23760,
26811, 30146, 33780, 37731, 42017, 46656, 50653, 55969, 60505, 66560,
71677, 78533, 84277, 91998, 98415, 107069, 114205, 123863, 131766, 142500,
151222, 163105, 172697, 185807, 196322, 210739, 222231, 238036, 250562, 267840,
281456, 300293, 315059, 335544, 351520, 373744, 390991, 415050, 433631, 459620,
479600, 507617, 529063, 559209, 582187, 614566, 639146, 673863, 700115, 737280,
765275, 804997, 834809, 877201, 908905, 954084, 987754, 1035837, 1071552, 1122660,
1160499, 1214753, 1254796, 1312322, 1354652, 1415577, 1460276, 1524731, 1571884, 1640000],
0, 4, 13, 32, 65, 112, 178, 276, 393, 540,
745, 967, 1230, 1591, 1957, 2457, 3046, 3732, 4526, 5440,
6482, 7666, 9003, 10506, 12187, 14060, 16140, 18439, 20974, 23760,
26811, 30146, 33780, 37731, 42017, 46656, 50653, 55969, 60505, 66560,
71677, 78533, 84277, 91998, 98415, 107069, 114205, 123863, 131766, 142500,
151222, 163105, 172697, 185807, 196322, 210739, 222231, 238036, 250562, 267840,
281456, 300293, 315059, 335544, 351520, 373744, 390991, 415050, 433631, 459620,
479600, 507617, 529063, 559209, 582187, 614566, 639146, 673863, 700115, 737280,
765275, 804997, 834809, 877201, 908905, 954084, 987754, 1035837, 1071552, 1122660,
1160499, 1214753, 1254796, 1312322, 1354652, 1415577, 1460276, 1524731, 1571884, 1640000],
:exp_formula => proc { |level|
rate = [82 - (level - 100) / 2.0, 40].max
next (level ** 4) * rate / 5000
next ((level**3) * ((level / 2) + 32)) * 4 / (100 + level)
}
})
@@ -142,49 +147,49 @@ GameData::GrowthRate.register({
:id => :Parabolic, # Also known as Medium Slow
:name => _INTL("Parabolic"),
:exp_values => [-1,
0, 9, 57, 96, 135, 179, 236, 314, 419, 560,
742, 973, 1261, 1612, 2035, 2535, 3120, 3798, 4575, 5460,
6458, 7577, 8825, 10208, 11735, 13411, 15244, 17242, 19411, 21760,
24294, 27021, 29949, 33084, 36435, 40007, 43808, 47846, 52127, 56660,
61450, 66505, 71833, 77440, 83335, 89523, 96012, 102810, 109923, 117360,
125126, 133229, 141677, 150476, 159635, 169159, 179056, 189334, 199999, 211060,
222522, 234393, 246681, 259392, 272535, 286115, 300140, 314618, 329555, 344960,
360838, 377197, 394045, 411388, 429235, 447591, 466464, 485862, 505791, 526260,
547274, 568841, 590969, 613664, 636935, 660787, 685228, 710266, 735907, 762160,
789030, 816525, 844653, 873420, 902835, 932903, 963632, 995030, 1027103, 1059860],
:exp_formula => proc { |level| next ((level ** 3) * 6 / 5) - 15 * (level ** 2) + 100 * level - 140 }
0, 9, 57, 96, 135, 179, 236, 314, 419, 560,
742, 973, 1261, 1612, 2035, 2535, 3120, 3798, 4575, 5460,
6458, 7577, 8825, 10208, 11735, 13411, 15244, 17242, 19411, 21760,
24294, 27021, 29949, 33084, 36435, 40007, 43808, 47846, 52127, 56660,
61450, 66505, 71833, 77440, 83335, 89523, 96012, 102810, 109923, 117360,
125126, 133229, 141677, 150476, 159635, 169159, 179056, 189334, 199999, 211060,
222522, 234393, 246681, 259392, 272535, 286115, 300140, 314618, 329555, 344960,
360838, 377197, 394045, 411388, 429235, 447591, 466464, 485862, 505791, 526260,
547274, 568841, 590969, 613664, 636935, 660787, 685228, 710266, 735907, 762160,
789030, 816525, 844653, 873420, 902835, 932903, 963632, 995030, 1027103, 1059860],
:exp_formula => proc { |level| next ((level**3) * 6 / 5) - (15 * (level**2)) + (100 * level) - 140 }
})
GameData::GrowthRate.register({
:id => :Fast,
:name => _INTL("Fast"),
:exp_values => [-1,
0, 6, 21, 51, 100, 172, 274, 409, 583, 800,
1064, 1382, 1757, 2195, 2700, 3276, 3930, 4665, 5487, 6400,
7408, 8518, 9733, 11059, 12500, 14060, 15746, 17561, 19511, 21600,
23832, 26214, 28749, 31443, 34300, 37324, 40522, 43897, 47455, 51200,
55136, 59270, 63605, 68147, 72900, 77868, 83058, 88473, 94119, 100000,
106120, 112486, 119101, 125971, 133100, 140492, 148154, 156089, 164303, 172800,
181584, 190662, 200037, 209715, 219700, 229996, 240610, 251545, 262807, 274400,
286328, 298598, 311213, 324179, 337500, 351180, 365226, 379641, 394431, 409600,
425152, 441094, 457429, 474163, 491300, 508844, 526802, 545177, 563975, 583200,
602856, 622950, 643485, 664467, 685900, 707788, 730138, 752953, 776239, 800000],
:exp_formula => proc { |level| (level ** 3) * 4 / 5 }
0, 6, 21, 51, 100, 172, 274, 409, 583, 800,
1064, 1382, 1757, 2195, 2700, 3276, 3930, 4665, 5487, 6400,
7408, 8518, 9733, 11059, 12500, 14060, 15746, 17561, 19511, 21600,
23832, 26214, 28749, 31443, 34300, 37324, 40522, 43897, 47455, 51200,
55136, 59270, 63605, 68147, 72900, 77868, 83058, 88473, 94119, 100000,
106120, 112486, 119101, 125971, 133100, 140492, 148154, 156089, 164303, 172800,
181584, 190662, 200037, 209715, 219700, 229996, 240610, 251545, 262807, 274400,
286328, 298598, 311213, 324179, 337500, 351180, 365226, 379641, 394431, 409600,
425152, 441094, 457429, 474163, 491300, 508844, 526802, 545177, 563975, 583200,
602856, 622950, 643485, 664467, 685900, 707788, 730138, 752953, 776239, 800000],
:exp_formula => proc { |level| (level**3) * 4 / 5 }
})
GameData::GrowthRate.register({
:id => :Slow,
:name => _INTL("Slow"),
:exp_values => [-1,
0, 10, 33, 80, 156, 270, 428, 640, 911, 1250,
1663, 2160, 2746, 3430, 4218, 5120, 6141, 7290, 8573, 10000,
11576, 13310, 15208, 17280, 19531, 21970, 24603, 27440, 30486, 33750,
37238, 40960, 44921, 49130, 53593, 58320, 63316, 68590, 74148, 80000,
86151, 92610, 99383, 106480, 113906, 121670, 129778, 138240, 147061, 156250,
165813, 175760, 186096, 196830, 207968, 219520, 231491, 243890, 256723, 270000,
283726, 297910, 312558, 327680, 343281, 359370, 375953, 393040, 410636, 428750,
447388, 466560, 486271, 506530, 527343, 548720, 570666, 593190, 616298, 640000,
664301, 689210, 714733, 740880, 767656, 795070, 823128, 851840, 881211, 911250,
941963, 973360, 1005446, 1038230, 1071718, 1105920, 1140841, 1176490, 1212873, 1250000],
:exp_formula => proc { |level| (level ** 3) * 5 / 4 }
0, 10, 33, 80, 156, 270, 428, 640, 911, 1250,
1663, 2160, 2746, 3430, 4218, 5120, 6141, 7290, 8573, 10000,
11576, 13310, 15208, 17280, 19531, 21970, 24603, 27440, 30486, 33750,
37238, 40960, 44921, 49130, 53593, 58320, 63316, 68590, 74148, 80000,
86151, 92610, 99383, 106480, 113906, 121670, 129778, 138240, 147061, 156250,
165813, 175760, 186096, 196830, 207968, 219520, 231491, 243890, 256723, 270000,
283726, 297910, 312558, 327680, 343281, 359370, 375953, 393040, 410636, 428750,
447388, 466560, 486271, 506530, 527343, 548720, 570666, 593190, 616298, 640000,
664301, 689210, 714733, 740880, 767656, 795070, 823128, 851840, 881211, 911250,
941963, 973360, 1005446, 1038230, 1071718, 1105920, 1140841, 1176490, 1212873, 1250000],
:exp_formula => proc { |level| (level**3) * 5 / 4 }
})

View File

@@ -1,7 +1,9 @@
#===============================================================================
# If a Pokémon's gender ratio is none of :AlwaysMale, :AlwaysFemale or
# :Genderless, then it will choose a random number between 0 and 255 inclusive,
# and compare it to the @female_chance. If the random number is lower than this
# chance, it will be female; otherwise, it will be male.
#===============================================================================
module GameData
class GenderRatio
attr_reader :id
@@ -16,6 +18,8 @@ module GameData
def self.load; end
def self.save; end
#---------------------------------------------------------------------------
def initialize(hash)
@id = hash[:id]
@real_name = hash[:name] || "Unnamed"
@@ -26,9 +30,17 @@ module GameData
def name
return _INTL(@real_name)
end
# @return [Boolean] whether a Pokémon with this gender ratio can only ever
# be a single gender
def single_gendered?
return @female_chance.nil?
end
end
end
#===============================================================================
#
#===============================================================================
GameData::GenderRatio.register({

View File

@@ -1,3 +1,6 @@
#===============================================================================
#
#===============================================================================
module GameData
class EggGroup
attr_reader :id
@@ -11,6 +14,8 @@ module GameData
def self.load; end
def self.save; end
#---------------------------------------------------------------------------
def initialize(hash)
@id = hash[:id]
@real_name = hash[:name] || "Unnamed"
@@ -23,6 +28,8 @@ module GameData
end
end
#===============================================================================
#
#===============================================================================
GameData::EggGroup.register({

View File

@@ -1,26 +1,29 @@
# NOTE: The id_number is only used to determine the order that body shapes are
# listed in the Pokédex search screen. Number 0 (:None) is ignored; they
# start with shape 1.
# "Graphics/Pictures/Pokedex/icon_shapes.png" contains icons for these
#===============================================================================
# NOTE: The order these shapes are registered are the order they are listed in
# the Pokédex search screen.
# "Graphics/UI/Pokedex/icon_shapes.png" contains icons for these
# shapes.
#===============================================================================
module GameData
class BodyShape
attr_reader :id
attr_reader :id_number
attr_reader :real_name
attr_reader :icon_position # Where this shape's icon is within icon_shapes.png
DATA = {}
extend ClassMethods
extend ClassMethodsSymbols
include InstanceMethods
def self.load; end
def self.save; end
#---------------------------------------------------------------------------
def initialize(hash)
@id = hash[:id]
@id_number = hash[:id_number] || -1
@real_name = hash[:name] || "Unnamed"
@id = hash[:id]
@real_name = hash[:name] || "Unnamed"
@icon_position = hash[:icon_position] || 0
end
# @return [String] the translated name of this body shape
@@ -30,88 +33,90 @@ module GameData
end
end
#===============================================================================
#
#===============================================================================
GameData::BodyShape.register({
:id => :Head,
:id_number => 1,
:name => _INTL("Head")
:id => :Head,
:name => _INTL("Head"),
:icon_position => 0
})
GameData::BodyShape.register({
:id => :Serpentine,
:id_number => 2,
:name => _INTL("Serpentine")
:id => :Serpentine,
:name => _INTL("Serpentine"),
:icon_position => 1
})
GameData::BodyShape.register({
:id => :Finned,
:id_number => 3,
:name => _INTL("Finned")
:id => :Finned,
:name => _INTL("Finned"),
:icon_position => 2
})
GameData::BodyShape.register({
:id => :HeadArms,
:id_number => 4,
:name => _INTL("Head and arms")
:id => :HeadArms,
:name => _INTL("Head and arms"),
:icon_position => 3
})
GameData::BodyShape.register({
:id => :HeadBase,
:id_number => 5,
:name => _INTL("Head and base")
:id => :HeadBase,
:name => _INTL("Head and base"),
:icon_position => 4
})
GameData::BodyShape.register({
:id => :BipedalTail,
:id_number => 6,
:name => _INTL("Bipedal with tail")
:id => :BipedalTail,
:name => _INTL("Bipedal with tail"),
:icon_position => 5
})
GameData::BodyShape.register({
:id => :HeadLegs,
:id_number => 7,
:name => _INTL("Head and legs")
:id => :HeadLegs,
:name => _INTL("Head and legs"),
:icon_position => 6
})
GameData::BodyShape.register({
:id => :Quadruped,
:id_number => 8,
:name => _INTL("Quadruped")
:id => :Quadruped,
:name => _INTL("Quadruped"),
:icon_position => 7
})
GameData::BodyShape.register({
:id => :Winged,
:id_number => 9,
:name => _INTL("Winged")
:id => :Winged,
:name => _INTL("Winged"),
:icon_position => 8
})
GameData::BodyShape.register({
:id => :Multiped,
:id_number => 10,
:name => _INTL("Multiped")
:id => :Multiped,
:name => _INTL("Multiped"),
:icon_position => 9
})
GameData::BodyShape.register({
:id => :MultiBody,
:id_number => 11,
:name => _INTL("Multi Body")
:id => :MultiBody,
:name => _INTL("Multi Body"),
:icon_position => 10
})
GameData::BodyShape.register({
:id => :Bipedal,
:id_number => 12,
:name => _INTL("Bipedal")
:id => :Bipedal,
:name => _INTL("Bipedal"),
:icon_position => 11
})
GameData::BodyShape.register({
:id => :MultiWinged,
:id_number => 13,
:name => _INTL("Multi Winged")
:id => :MultiWinged,
:name => _INTL("Multi Winged"),
:icon_position => 12
})
GameData::BodyShape.register({
:id => :Insectoid,
:id_number => 14,
:name => _INTL("Insectoid")
:id => :Insectoid,
:name => _INTL("Insectoid"),
:icon_position => 13
})

View File

@@ -1,23 +1,25 @@
# NOTE: The id_number is only used to determine the order that body colors are
# listed in the Pokédex search screen.
#===============================================================================
# NOTE: The order these colors are registered are the order they are listed in
# the Pokédex search screen.
#===============================================================================
module GameData
class BodyColor
attr_reader :id
attr_reader :id_number
attr_reader :real_name
DATA = {}
extend ClassMethods
extend ClassMethodsSymbols
include InstanceMethods
def self.load; end
def self.save; end
#---------------------------------------------------------------------------
def initialize(hash)
@id = hash[:id]
@id_number = hash[:id_number] || -1
@real_name = hash[:name] || "Unnamed"
@real_name = hash[:name] || "Unnamed"
end
# @return [String] the translated name of this body color
@@ -27,64 +29,56 @@ module GameData
end
end
#===============================================================================
#
#===============================================================================
GameData::BodyColor.register({
:id => :Red,
:id_number => 0,
:name => _INTL("Red")
:id => :Red,
:name => _INTL("Red")
})
GameData::BodyColor.register({
:id => :Blue,
:id_number => 1,
:name => _INTL("Blue")
:id => :Blue,
:name => _INTL("Blue")
})
GameData::BodyColor.register({
:id => :Yellow,
:id_number => 2,
:name => _INTL("Yellow")
:id => :Yellow,
:name => _INTL("Yellow")
})
GameData::BodyColor.register({
:id => :Green,
:id_number => 3,
:name => _INTL("Green")
:id => :Green,
:name => _INTL("Green")
})
GameData::BodyColor.register({
:id => :Black,
:id_number => 4,
:name => _INTL("Black")
:id => :Black,
:name => _INTL("Black")
})
GameData::BodyColor.register({
:id => :Brown,
:id_number => 5,
:name => _INTL("Brown")
:id => :Brown,
:name => _INTL("Brown")
})
GameData::BodyColor.register({
:id => :Purple,
:id_number => 6,
:name => _INTL("Purple")
:id => :Purple,
:name => _INTL("Purple")
})
GameData::BodyColor.register({
:id => :Gray,
:id_number => 7,
:name => _INTL("Gray")
:id => :Gray,
:name => _INTL("Gray")
})
GameData::BodyColor.register({
:id => :White,
:id_number => 8,
:name => _INTL("White")
:id => :White,
:name => _INTL("White")
})
GameData::BodyColor.register({
:id => :Pink,
:id_number => 9,
:name => _INTL("Pink")
:id => :Pink,
:name => _INTL("Pink")
})

View File

@@ -1,3 +1,6 @@
#===============================================================================
#
#===============================================================================
module GameData
class Habitat
attr_reader :id
@@ -11,6 +14,8 @@ module GameData
def self.load; end
def self.save; end
#---------------------------------------------------------------------------
def initialize(hash)
@id = hash[:id]
@real_name = hash[:name] || "Unnamed"
@@ -23,6 +28,8 @@ module GameData
end
end
#===============================================================================
#
#===============================================================================
GameData::Habitat.register({

View File

@@ -1,12 +1,18 @@
#===============================================================================
#
#===============================================================================
module GameData
class Evolution
attr_reader :id
attr_reader :real_name
attr_reader :parameter
attr_reader :minimum_level # 0 means parameter is the minimum level
attr_reader :any_level_up # false means parameter is the minimum level
attr_reader :level_up_proc
attr_reader :battle_level_up_proc
attr_reader :use_item_proc
attr_reader :on_trade_proc
attr_reader :after_battle_proc
attr_reader :event_proc
attr_reader :after_evolution_proc
DATA = {}
@@ -17,21 +23,37 @@ module GameData
def self.load; end
def self.save; end
#---------------------------------------------------------------------------
def initialize(hash)
@id = hash[:id]
@real_name = hash[:id].to_s || "Unnamed"
@real_name = hash[:id].to_s || "Unnamed"
@parameter = hash[:parameter]
@minimum_level = hash[:minimum_level] || 0
@any_level_up = hash[:any_level_up] || false
@level_up_proc = hash[:level_up_proc]
@battle_level_up_proc = hash[:battle_level_up_proc]
@use_item_proc = hash[:use_item_proc]
@on_trade_proc = hash[:on_trade_proc]
@after_battle_proc = hash[:after_battle_proc]
@event_proc = hash[:event_proc]
@after_evolution_proc = hash[:after_evolution_proc]
end
alias name real_name
def call_level_up(*args)
return (@level_up_proc) ? @level_up_proc.call(*args) : nil
end
def call_battle_level_up(*args)
if @battle_level_up_proc
return @battle_level_up_proc.call(*args)
elsif @level_up_proc
return @level_up_proc.call(*args)
end
return nil
end
def call_use_item(*args)
return (@use_item_proc) ? @use_item_proc.call(*args) : nil
end
@@ -40,18 +62,32 @@ module GameData
return (@on_trade_proc) ? @on_trade_proc.call(*args) : nil
end
def call_after_battle(*args)
return (@after_battle_proc) ? @after_battle_proc.call(*args) : nil
end
def call_event(*args)
return (@event_proc) ? @event_proc.call(*args) : nil
end
def call_after_evolution(*args)
@after_evolution_proc.call(*args) if @after_evolution_proc
@after_evolution_proc&.call(*args)
end
end
end
#===============================================================================
#
#===============================================================================
GameData::Evolution.register({
:id => :None
})
#===============================================================================
#
#===============================================================================
GameData::Evolution.register({
:id => :Level,
:parameter => Integer,
@@ -184,12 +220,23 @@ GameData::Evolution.register({
}
})
GameData::Evolution.register({
:id => :LevelCoins,
:parameter => Integer,
:level_up_proc => proc { |pkmn, parameter|
next $player.coins >= parameter
},
:after_evolution_proc => proc { |pkmn, new_species, parameter, evo_species|
$player.coins -= parameter
next true
}
})
GameData::Evolution.register({
:id => :LevelDarkness,
:parameter => Integer,
:level_up_proc => proc { |pkmn, parameter|
map_metadata = GameData::MapMetadata.try_get($game_map.map_id)
next pkmn.level >= parameter && map_metadata && map_metadata.dark_map
next pkmn.level >= parameter && $game_map.metadata&.dark_map
}
})
@@ -197,7 +244,7 @@ GameData::Evolution.register({
:id => :LevelDarkInParty,
:parameter => Integer,
:level_up_proc => proc { |pkmn, parameter|
next pkmn.level >= parameter && $Trainer.has_pokemon_of_type?(:DARK)
next pkmn.level >= parameter && $player.has_pokemon_of_type?(:DARK, [pkmn])
}
})
@@ -252,64 +299,61 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :Shedinja,
:parameter => Integer,
:level_up_proc => proc { |pkmn, parameter|
next false # This is a dummy proc and shouldn't next true
},
:after_evolution_proc => proc { |pkmn, new_species, parameter, evo_species|
next false if $Trainer.party_full?
next false if !$PokemonBag.pbHasItem?(:POKEBALL)
next false if $player.party_full?
next false if !$bag.has?(:POKEBALL)
PokemonEvolutionScene.pbDuplicatePokemon(pkmn, new_species)
$PokemonBag.pbDeleteItem(:POKEBALL)
$bag.remove(:POKEBALL)
next true
}
})
GameData::Evolution.register({
:id => :Happiness,
:minimum_level => 1, # Needs any level up
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.happiness >= 220
next pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220)
}
})
GameData::Evolution.register({
:id => :HappinessMale,
:minimum_level => 1, # Needs any level up
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.happiness >= 220 && pkmn.male?
next pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220) && pkmn.male?
}
})
GameData::Evolution.register({
:id => :HappinessFemale,
:minimum_level => 1, # Needs any level up
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.happiness >= 220 && pkmn.female?
next pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220) && pkmn.female?
}
})
GameData::Evolution.register({
:id => :HappinessDay,
:minimum_level => 1, # Needs any level up
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.happiness >= 220 && PBDayNight.isDay?
next pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220) && PBDayNight.isDay?
}
})
GameData::Evolution.register({
:id => :HappinessNight,
:minimum_level => 1, # Needs any level up
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.happiness >= 220 && PBDayNight.isNight?
next pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220) && PBDayNight.isNight?
}
})
GameData::Evolution.register({
:id => :HappinessMove,
:parameter => :Move,
:minimum_level => 1, # Needs any level up
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
if pkmn.happiness >= 220
if pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220)
next pkmn.moves.any? { |m| m && m.id == parameter }
end
}
@@ -318,10 +362,10 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :HappinessMoveType,
:parameter => :Type,
:minimum_level => 1, # Needs any level up
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
if pkmn.happiness >= 220
next pkmn.moves.any? { |m| m && m.id > 0 && m.type == parameter }
if pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220)
next pkmn.moves.any? { |m| m && m.type == parameter }
end
}
})
@@ -329,9 +373,9 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :HappinessHoldItem,
:parameter => :Item,
:minimum_level => 1, # Needs any level up
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.item == parameter && pkmn.happiness >= 220
next pkmn.item == parameter && pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220)
},
:after_evolution_proc => proc { |pkmn, new_species, parameter, evo_species|
next false if evo_species != new_species || !pkmn.hasItem?(parameter)
@@ -342,7 +386,7 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :MaxHappiness,
:minimum_level => 1, # Needs any level up
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.happiness == 255
}
@@ -351,7 +395,7 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :Beauty, # Feebas
:parameter => Integer,
:minimum_level => 1, # Needs any level up
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.beauty >= parameter
}
@@ -360,7 +404,7 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :HoldItem,
:parameter => :Item,
:minimum_level => 1, # Needs any level up
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.item == parameter
},
@@ -374,7 +418,7 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :HoldItemMale,
:parameter => :Item,
:minimum_level => 1, # Needs any level up
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.item == parameter && pkmn.male?
},
@@ -388,7 +432,7 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :HoldItemFemale,
:parameter => :Item,
:minimum_level => 1, # Needs any level up
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.item == parameter && pkmn.female?
},
@@ -402,7 +446,7 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :DayHoldItem,
:parameter => :Item,
:minimum_level => 1, # Needs any level up
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.item == parameter && PBDayNight.isDay?
},
@@ -416,7 +460,7 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :NightHoldItem,
:parameter => :Item,
:minimum_level => 1, # Needs any level up
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.item == parameter && PBDayNight.isNight?
},
@@ -430,9 +474,9 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :HoldItemHappiness,
:parameter => :Item,
:minimum_level => 1, # Needs any level up
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.item == parameter && pkmn.happiness >= 220
next pkmn.item == parameter && pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220)
},
:after_evolution_proc => proc { |pkmn, new_species, parameter, evo_species|
next false if evo_species != new_species || !pkmn.hasItem?(parameter)
@@ -444,7 +488,7 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :HasMove,
:parameter => :Move,
:minimum_level => 1, # Needs any level up
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.moves.any? { |m| m && m.id == parameter }
}
@@ -453,7 +497,7 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :HasMoveType,
:parameter => :Type,
:minimum_level => 1, # Needs any level up
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.moves.any? { |m| m && m.type == parameter }
}
@@ -462,35 +506,69 @@ GameData::Evolution.register({
GameData::Evolution.register({
:id => :HasInParty,
:parameter => :Species,
:minimum_level => 1, # Needs any level up
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next $Trainer.has_species?(parameter)
next $player.has_species?(parameter)
}
})
GameData::Evolution.register({
:id => :Location,
:parameter => Integer,
:minimum_level => 1, # Needs any level up
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next $game_map.map_id == parameter
}
})
GameData::Evolution.register({
:id => :LocationFlag,
:parameter => String,
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next $game_map.metadata&.has_flag?(parameter)
}
})
GameData::Evolution.register({
:id => :Region,
:parameter => Integer,
:minimum_level => 1, # Needs any level up
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
map_metadata = GameData::MapMetadata.try_get($game_map.map_id)
next map_metadata && map_metadata.town_map_position &&
map_metadata.town_map_position[0] == parameter
map_metadata = $game_map.metadata
next map_metadata&.town_map_position && map_metadata.town_map_position[0] == parameter
}
})
GameData::Evolution.register({
:id => :Counter,
:parameter => Integer,
:any_level_up => true, # Needs any level up
:level_up_proc => proc { |pkmn, parameter|
next pkmn.evolution_counter >= parameter
},
:after_evolution_proc => proc { |pkmn, new_species, parameter, evo_species|
pkmn.evolution_counter = 0
next true
}
})
#===============================================================================
# Evolution methods that trigger when using an item on the Pokémon
# Evolution methods that trigger when levelling up in battle.
#===============================================================================
GameData::Evolution.register({
:id => :LevelBattle,
:parameter => Integer,
:battle_level_up_proc => proc { |pkmn, parameter|
next pkmn.level >= parameter
}
})
#===============================================================================
# Evolution methods that trigger when using an item on the Pokémon.
#===============================================================================
GameData::Evolution.register({
:id => :Item,
:parameter => :Item,
@@ -535,13 +613,14 @@ GameData::Evolution.register({
:id => :ItemHappiness,
:parameter => :Item,
:use_item_proc => proc { |pkmn, parameter, item|
next item == parameter && pkmn.happiness >= 220
next item == parameter && pkmn.happiness >= (Settings::APPLY_HAPPINESS_SOFT_CAP ? 160 : 220)
}
})
#===============================================================================
# Evolution methods that trigger when the Pokémon is obtained in a trade
# Evolution methods that trigger when the Pokémon is obtained in a trade.
#===============================================================================
GameData::Evolution.register({
:id => :Trade,
:on_trade_proc => proc { |pkmn, parameter, other_pkmn|
@@ -594,6 +673,72 @@ GameData::Evolution.register({
:id => :TradeSpecies,
:parameter => :Species,
:on_trade_proc => proc { |pkmn, parameter, other_pkmn|
next pkmn.species == parameter && !other_pkmn.hasItem?(:EVERSTONE)
next other_pkmn.species == parameter && !other_pkmn.hasItem?(:EVERSTONE)
}
})
#===============================================================================
# Evolution methods that are triggered after any battle.
#===============================================================================
GameData::Evolution.register({
:id => :AfterBattleCounter,
:parameter => Integer,
:any_level_up => true, # Needs any level up
:after_battle_proc => proc { |pkmn, party_index, parameter|
ret = pkmn.evolution_counter >= parameter
pkmn.evolution_counter = 0 # Always resets after battle
next ret
}
})
# Doesn't cause an evolution itself. Just makes the Pokémon ready to evolve by
# another means (e.g. via an event). Note that pkmn.evolution_counter is not
# reset after the battle.
GameData::Evolution.register({
:id => :AfterBattleCounterMakeReady,
:parameter => Integer,
:any_level_up => true, # Needs any level up
:after_battle_proc => proc { |pkmn, party_index, parameter|
pkmn.ready_to_evolve = true if pkmn.evolution_counter >= parameter
next false
}
})
#===============================================================================
# Evolution methods that are triggered by an event.
# Each event has its own number, which is the value of the parameter as defined
# in pokemon.txt/pokemon_forms.txt. It is also 'number' in def pbEvolutionEvent,
# which triggers evolution checks for a particular event number. 'value' in an
# event_proc is the number of the evolution event currently being triggered.
# Evolutions caused by different events should have different numbers. Used
# event numbers are:
# 1: Kubfu -> Urshifu
# 2: Galarian Yamask -> Runerigus
# These used event numbers are only used in pokemon.txt/pokemon_forms.txt and in
# map events that call pbEvolutionEvent, so they are relatively easy to change
# if you need to (no script changes are required). However, you could just
# ignore them instead if you don't want to use them.
#===============================================================================
def pbEvolutionEvent(number)
return if !$player
$player.able_party.each do |pkmn|
pkmn.trigger_event_evolution(number)
end
end
GameData::Evolution.register({
:id => :Event,
:parameter => Integer,
:event_proc => proc { |pkmn, parameter, value|
next value == parameter
}
})
GameData::Evolution.register({
:id => :EventReady,
:parameter => Integer,
:event_proc => proc { |pkmn, parameter, value|
next value == parameter && pkmn.ready_to_evolve
}
})

Some files were not shown because too many files have changed in this diff Show More