Описание мода «Loji DEX»
Loji DEX улучшает геймплей Age of History 2 путем правки игрового кода, цель проекта — создать базу для создания модификаций на системе Android.
Список вносимых изменений:
- Поддержка GIF-анимаций для главного меню, лидеров, событий и HOG.
- Можно переименновывать города.
- Улучшения альянсов/фракций.
- Система группировки подразделений.
- Система волонтеров.
- Ядерное оружие / Ядерная система.
- Шпаргалка по определению размера провинции.
- Обновленные читы.
- Извлечение данных в документы.
- Шаблоны TNO / TFR (Шаблон RF скоро будет доступен)
- Улучшен пользовательский интерфейс для копирования, вставки и многого другого.
Описание обновлений
16.07.26:
+ можно переименовывать города для получения результатов событий.
+ улучшен пользовательский интерфейс для копирования, вставки и многого другого.
18.07.26:
+ Суперсобитие отделено от событий.
+ Мир только с одной страной.
+ Исправление для клавиатуры.
21.08.26:
+ Шаблоны TFR/TNO.
+ Главное меню.
+ Экран загрузки.
Релиз (11.09.26):
+ Поддержка GIF-анимаций для главного меню, лидеров, событий и HOG.
+ 3× очка передвижения.
+ Улучшения альянсов/фракций:
Переименовать альянсы,
Изменить цвета альянса,
Вновь созданные фракции автоматически наследуют цвет лидера фракции.
+ Система группировки подразделений.
+ Система волонтеров.
+ Ядерное оружие / Ядерная система.
+ Шпаргалка по определению размера провинции.
+ Обновленные читы.
+ Извлечение данных в документы — спасибо @Noiru
Мини обновление (15.09.26):
+ В меню «Аудио» теперь есть строка поиска, и вы можете выбирать музыку напрямую.
+ В разделе «Редактирование провинций» и «Подключения» теперь больше кнопок, что упрощает редактирование карт.
+ Улучшена работа с альянсами. Теперь вместо названий стран будет отображаться название альянса.
+ Функция «Белый мир» была восстановлена. «Белый мир» теперь представляет собой отдельный результат, называемый «Peace All».
+ Система добровольцев исправлена. Подразделения больше не исчезают.
+ Исправлена проблема утечки/сбоя событий.
+ Теперь также доступна предустановленная APK-версия.
Руководство для модеров (ENG)
Loji Classes and Files Guideline
Installing event features with MT Manager on Android
This guide is based on the following donor APK:
D:\Gameapk\Input\The Millennium Shift_0.1.apk
SHA-256: 5D7E7C905FEA4CD4515BA53F0B38370F9308AEB2AF8AA24C642141CC615C35C3 The APK contains two DEX files:
classes.dex = game code and every feature covered by this guide
classes2.dex = MTDataFilesProvider and MTDataFilesWakeUpActivity classes2.dex is an MT Manager integration component. It does not contain the game features described below.
Covered features:
- Rename City.
- Update Leader and Head of Government.
- Animated GIF leaders.
- National and base ideology titles, descriptions, and pictures.
- Peace out with one country.
- Android native UI keyboard.
- Separate Super Event renderer.
- Normal Event and Super Event selection.
- Unrestricted Trigger Another Event selection.
1. Prepare the files on Android
Install MT Manager and create a working directory:
/storage/emulated/0/LojiDexWork/ Put these files in it:
donor.apk = The Millennium Shift_0.1.apk containing the features
target.apk = the APK that will receive the features
target-backup.apk = an untouched backup Use MT Manager in dual-panel mode:
- Open
target.apkin the left panel. - Open
donor.apkin the right panel. - Open
classes.dexand choose Dex Editor++. - Use class search to locate each class listed in this guide.
The game package is:
age.of.civilizations2.jakowski.lukasz The corresponding Smali descriptor is:
Lage/of/civilizations2/jakowski/lukasz/ClassName; Example:
Java name: Event_Outcome_RenameCity
Smali name: Lage/of/civilizations2/jakowski/lukasz/Event_Outcome_RenameCity; Option A: replace the complete classes.dex
Use this option only when the target uses the same engine/base and should receive every donor feature.
- Extract
classes.dexfromdonor.apk. - Open
target.apkas an archive. - Replace the target
classes.dex. - Copy the required assets listed in this guide.
- Sign the APK.
This method overwrites every custom code change already present in the target DEX.
Option B: import and merge selected classes
Use this option when the target has its own code changes.
- Extract the donor
classes.dex. - Open the target
classes.dexin Dex Editor++. - Choose Add, Import classes from dex, or the equivalent command.
- Select the donor DEX.
- Import new classes.
- Back up existing classes before merging modified methods into them.
Do not replace central classes such as CFG, MenuManager, EventsManager, or Keyboard with versions from an unrelated engine fork. Merge the required blocks instead.
2. Dependency map
| Feature | New or primary classes | Existing classes that require merging |
|---|---|---|
| Rename City | Event_Outcome_RenameCity, Menu_CreateScenario_Events_Out_RenameCity, Menu_CreateScenario_Events_SelectCity | Menu_CreateScenario_Events_AddNewOutcome, EventsManager, MenuManager |
| Head of Government | HeadOfGovernmentSupport | Event_Outcome_Leader, leader selection menus, EventsManager, MenuManager, Menu_InGame_CivInfo$2, Ideologies_Manager, Event_Outcome_ChangeIdeology |
| Animated GIF leader | GifLeaderSupport, GifLeaderSupport$GifState | CFG, Menu_InGame_CivInfo_Stats, Menu_Civilization_Info_Stats, MenuElement_Hover_v2_Element_Type_Leader |
| Ideology content | HeadOfGovernmentSupport | Ideologies_Manager, Bundle.properties, assets/game/hog/ |
| White Peace | Event_Outcome_WhitePeace, Menu_CreateScenario_Events_Out_WhitePeace | Menu_CreateScenario_Events_AddNewOutcome, MenuManager |
| Native keyboard | NativeKeyboardBridge and its six inner classes | Keyboard.setVisible(Z) |
| Separate Super Event | Menu_InGame_Event_Super, $1, $2 | Event_GameData, MenuManager, assets/UI/events/ |
| Normal/Super picker | uses the Super Event classes | Event_GameData, Menu_CreateScenario_Events_Edit, MenuManager |
| Trigger Another Event | trigger outcome and event selection menus | Menu_CreateScenario_Events_AddNewOutcome, MenuManager |
3. Rename City
3.1 Import the classes
Import:
Lage/of/civilizations2/jakowski/lukasz/Event_Outcome_RenameCity;
Lage/of/civilizations2/jakowski/lukasz/Menu_CreateScenario_Events_Out_RenameCity;
Lage/of/civilizations2/jakowski/lukasz/Menu_CreateScenario_Events_SelectCity; Merge the compatible blocks from:
Menu_CreateScenario_Events_AddNewOutcome
EventsManager
MenuManager 3.2 Required outcome fields
Event_Outcome_RenameCity must contain:
.field private applied:Z
.field private cityIndex:I
.field private newName:Ljava/lang/String;
.field private provinceID:I 3.3 Rename action
The action belongs in:
.method protected outcomeAction()V Core code:
sget-object v0, Lage/of/civilizations2/jakowski/lukasz/CFG;->game:Lage/of/civilizations2/jakowski/lukasz/Game;
iget v1, p0, Lage/of/civilizations2/jakowski/lukasz/Event_Outcome_RenameCity;->provinceID:I
invoke-virtual {v0, v1}, Lage/of/civilizations2/jakowski/lukasz/Game;->getProvince(I)Lage/of/civilizations2/jakowski/lukasz/Province;
move-result-object v0
iget v1, p0, Lage/of/civilizations2/jakowski/lukasz/Event_Outcome_RenameCity;->cityIndex:I
invoke-virtual {v0, v1}, Lage/of/civilizations2/jakowski/lukasz/Province;->getCity(I)Lage/of/civilizations2/jakowski/lukasz/City;
move-result-object v0
iget-object v1, p0, Lage/of/civilizations2/jakowski/lukasz/Event_Outcome_RenameCity;->newName:Ljava/lang/String;
invoke-virtual {v1}, Ljava/lang/String;->trim()Ljava/lang/String;
move-result-object v1
invoke-virtual {v0, v1}, Lage/of/civilizations2/jakowski/lukasz/City;->setCityName(Ljava/lang/String;)V
invoke-virtual {v0}, Lage/of/civilizations2/jakowski/lukasz/City;->updateCityNameWidth()V Do not remove updateCityNameWidth(). The displayed width may remain tied to the old name otherwise.
3.4 Open the correct editor
EventsManager.editOutcome() must detect the custom outcome:
instance-of v0, v0, Lage/of/civilizations2/jakowski/lukasz/Event_Outcome_RenameCity;
if-eqz v0, :not_rename_city
sget-object v0, Lage/of/civilizations2/jakowski/lukasz/CFG;->menuManager:Lage/of/civilizations2/jakowski/lukasz/MenuManager;
const/4 v1, 0x2
invoke-virtual {v0, v1}, Lage/of/civilizations2/jakowski/lukasz/MenuManager;->setNewView(I)V
return-void In the donor build, MenuManager.setNewView(I) uses:
2 = Menu_CreateScenario_Events_Out_RenameCity
3 = Menu_CreateScenario_Events_SelectCity If those IDs are already used in the target, choose free IDs and update every caller consistently.
4. Update Leader and Head of Government
The selected leader is stored in the current save’s civilization data. It does not overwrite the serialized leader file under assets/game/leaders/. A new save starts from the normal data.
4.1 Import and merge
Import the new helper:
HeadOfGovernmentSupport Import or merge compatible versions of:
Event_Outcome_Leader
Event_Outcome_Leader$1
Event_Outcome_Leader$2
Event_Outcome_Leader$3
Menu_CreateScenario_Events_Out_Leader
Menu_CreateScenario_Events_SelectLeader
Menu_CreateScenario_Events_SelectLeader_Title
Menu_CreateScenario_Events_SelectLeader_Alphabet
EventsManager
MenuManager
Menu_InGame_CivInfo$2
Ideologies_Manager
Event_Outcome_ChangeIdeology
Menu_CreateScenario_Events_AddNewOutcome 4.2 Run the HOG helper
At the start of:
Lage/of/civilizations2/jakowski/lukasz/Event_Outcome_Leader;->outcomeAction()V call:
invoke-static {p0}, Lage/of/civilizations2/jakowski/lukasz/HeadOfGovernmentSupport;->applyOutcome(Lage/of/civilizations2/jakowski/lukasz/Event_Outcome_Leader;)V
return-void 4.3 Select a leader from the complete leader source
In both selection branches of:
Menu_CreateScenario_Events_SelectLeader.actionElement(I) pass the deserialized Leader_GameData through the source converter:
check-cast v4, Lage/of/civilizations2/jakowski/lukasz/Leader_GameData;
invoke-static {v4}, Lage/of/civilizations2/jakowski/lukasz/HeadOfGovernmentSupport;->prepareLeaderFromSource(Lage/of/civilizations2/jakowski/lukasz/Leader_GameData;)Lage/of/civilizations2/jakowski/lukasz/LeaderOfCiv_GameData;
move-result-object v4
invoke-virtual {v2, v4}, Lage/of/civilizations2/jakowski/lukasz/Event_Outcome_Leader;->setLeader(Lage/of/civilizations2/jakowski/lukasz/LeaderOfCiv_GameData;)V Adapt the registers to the target method. Do not paste v2 and v4 blindly.
4.4 Leader Bundle keys
Edit:
assets/game/languages/Bundle.properties Format:
<LeaderBundlePrefix>_gov = #HEX/Head of Government:
<LeaderBundlePrefix>_gov_leader = #HEX/Government title:\nLeader name.
<LeaderBundlePrefix>_gov_leader_pic = game/leadersIMG/image.png
<LeaderBundlePrefix>_gov_leader_desc = Description of the selected leader Example:
ukntde_example_gov = #0f79bf/Head of Government:
ukntde_example_gov_leader = #0f79bf/British Monarchy:\nElizabeth II.
ukntde_example_gov_leader_pic = game/leadersIMG/ElizabethII.png
ukntde_example_gov_leader_desc = Queen #C3B091/Elizabeth II\nhas served as constitutional monarch. The picture belongs at:
assets/game/leadersIMG/ElizabethII.png 4.5 Refresh all displayed leader values
Menu_InGame_CivInfo$2 must call the helper for every displayed value:
invoke-static {v2}, Lage/of/civilizations2/jakowski/lukasz/HeadOfGovernmentSupport;->resolveGovernmentTitle(Ljava/lang/String;)Ljava/lang/String;
invoke-static {v2}, Lage/of/civilizations2/jakowski/lukasz/HeadOfGovernmentSupport;->resolveName(Ljava/lang/String;)Ljava/lang/String;
invoke-static {v2}, Lage/of/civilizations2/jakowski/lukasz/HeadOfGovernmentSupport;->resolvePicture(Ljava/lang/String;)Ljava/lang/String;
invoke-static {v2}, Lage/of/civilizations2/jakowski/lukasz/HeadOfGovernmentSupport;->resolveDescription(Ljava/lang/String;)Ljava/lang/String; Calling only resolveName() changes the leader name but leaves the title, picture, and description stale.
The helper recovers a scenario-specific Bundle prefix by matching the selected leader picture against every *_gov_leader_pic key. Matching uses the filename stem, so lojistar.png in serialized leader data can match lojistar.gif in Bundle.
5. Animated GIF leaders
GIF leaders are animated in this APK. They are not limited to the first frame.
5.1 Asset and Bundle entry
Put the file at:
assets/game/leadersIMG/lojistar.gif Reference it in Bundle:
chinantde_example_gov = #0f79bf/Head of Government:
chinantde_example_gov_leader = #0f79bf/Great Entity:\nLoji.
chinantde_example_gov_leader_pic = game/leadersIMG/lojistar.gif
chinantde_example_gov_leader_desc = Loji leader description. Rules:
- The leader image name must end with
.gifto enter the animation branch. - The file must contain GIF data that Android
Movie.decodeStream()can decode. - APK asset names are case-sensitive.
- Use
game/leadersIMG/...in Bundle, without anassets/prefix.
The verified donor file is:
File: assets/game/leadersIMG/lojistar.gif
Header: GIF89a
Size: 156 x 210
Frames: 90
Total duration: 8100 ms
Loop: infinite 5.2 Required classes and hooks
Import:
GifLeaderSupport
GifLeaderSupport$GifState Merge the hooks in:
CFG
Menu_InGame_CivInfo_Stats
Menu_Civilization_Info_Stats
MenuElement_Hover_v2_Element_Type_Leader Adding only the GIF file and Bundle key is not enough when the target does not already contain these classes and hooks.
5.3 GIF suffix detection
The branch is inside:
GifLeaderSupport.loadLeader(String imageName) sget-object v1, Ljava/util/Locale;->US:Ljava/util/Locale;
invoke-virtual {p0, v1}, Ljava/lang/String;->toLowerCase(Ljava/util/Locale;)Ljava/lang/String;
move-result-object v1
const-string v2, ".gif"
invoke-virtual {v1, v2}, Ljava/lang/String;->endsWith(Ljava/lang/String;)Z
move-result v1
if-nez v1, :load_animated_gif Non-GIF images continue through the normal Texture(FileHandle) loader. The GIF branch opens the file and invokes Android’s decoder:
const-string v0, "android.graphics.Movie"
invoke-static {v0}, Ljava/lang/Class;->forName(Ljava/lang/String;)Ljava/lang/Class;
const-string v0, "decodeStream"
# Reflection invokes Movie.decodeStream(InputStream) 5.4 Frame timing and texture updates
GifLeaderSupport$GifState.update(Texture) throttles updates to approximately 30 FPS:
invoke-static {}, Ljava/lang/System;->currentTimeMillis()J
move-result-wide v0
iget-wide v2, p0, Lage/of/civilizations2/jakowski/lukasz/GifLeaderSupport$GifState;->lastUpdate:J
sub-long v2, v0, v2
const-wide/16 v4, 0x21
cmp-long v2, v2, v4
if-gez v2, :update_gif_frame 0x21 is 33 milliseconds. Playback time loops over the GIF duration:
iget-wide v2, p0, Lage/of/civilizations2/jakowski/lukasz/GifLeaderSupport$GifState;->startedAt:J
sub-long/2addr v0, v2
iget v2, p0, Lage/of/civilizations2/jakowski/lukasz/GifLeaderSupport$GifState;->duration:I
int-to-long v2, v2
rem-long/2addr v0, v2 renderInto(Pixmap, time) calls Movie.setTime(time), draws onto an Android Canvas, copies ARGB pixels, converts them to RGBA, and updates the libGDX texture:
invoke-virtual {p0, v1, v0}, Lage/of/civilizations2/jakowski/lukasz/GifLeaderSupport$GifState;->renderInto(Lcom/badlogic/gdx/graphics/Pixmap;I)V
invoke-virtual {p1, v0, v1, v2}, Lcom/badlogic/gdx/graphics/Texture;->draw(Lcom/badlogic/gdx/graphics/Pixmap;II)V 5.5 Load and draw hooks
When CFG loads activeCivLeader, it passes leaderData.getImage() to:
invoke-static {p0}, Lage/of/civilizations2/jakowski/lukasz/GifLeaderSupport;->loadLeader(Ljava/lang/String;)Lage/of/civilizations2/jakowski/lukasz/Image;
move-result-object p0
if-eqz p0, :use_original_image_loader
sput-object p0, Lage/of/civilizations2/jakowski/lukasz/CFG;->activeCivLeader:Lage/of/civilizations2/jakowski/lukasz/Image; Each leader renderer updates the frame before drawing:
invoke-static {v1}, Lage/of/civilizations2/jakowski/lukasz/GifLeaderSupport;->beforeDraw(Lage/of/civilizations2/jakowski/lukasz/Image;)V Verified draw hooks:
Menu_InGame_CivInfo_Stats
Menu_Civilization_Info_Stats
MenuElement_Hover_v2_Element_Type_Leader beforeDraw() updates the texture and keeps the render loop active:
const/4 v0, 0x1
invoke-static {v0}, Lage/of/civilizations2/jakowski/lukasz/CFG;->setRender_3(Z)V 5.6 Dispose GIF resources
CFG.disposeActiveCivLeader() must call:
sget-object v0, Lage/of/civilizations2/jakowski/lukasz/CFG;->activeCivLeader:Lage/of/civilizations2/jakowski/lukasz/Image;
invoke-static {v0}, Lage/of/civilizations2/jakowski/lukasz/GifLeaderSupport;->dispose(Lage/of/civilizations2/jakowski/lukasz/Image;)V The helper removes the GifState, disposes the Pixmap, recycles the Android Bitmap, and then allows the normal texture disposal to continue.
6. National and base ideology content
This section controls the Head of Government ideology hover. It is separate from the leader picture system.
6.1 Code hook
Required classes:
HeadOfGovernmentSupport
Ideologies_Manager At the start of:
Lage/of/civilizations2/jakowski/lukasz/Ideologies_Manager;->getIdeologyHover(I)Lage/of/civilizations2/jakowski/lukasz/MenuElement_Hover_v2; call:
invoke-static {p1}, Lage/of/civilizations2/jakowski/lukasz/HeadOfGovernmentSupport;->buildIdeologyHover(I)Lage/of/civilizations2/jakowski/lukasz/MenuElement_Hover_v2;
move-result-object v0
if-eqz v0, :use_original_hover
return-object v0
:use_original_hover
# Original hover code continues here. 6.2 Picture location
Create:
assets/game/hog/ Pictures can be stored directly or in subfolders:
assets/game/hog/trumpxloji.png
assets/game/hog/us/thirdway.png
assets/game/hog/uk/monarchy.png 6.3 National ideology namespace
Format:
<NationalTag>_<IdeologyName>_gov = #HEX/Title
<NationalTag>_<IdeologyName>_desc = Description
<NationalTag>_<IdeologyName>_pics = game/hog/folder/image.png Example for the United States:
usantde_Neoliberalism-Thirdway_gov = #FFFFFF/Neoliberalism-Thirdway
usantde_Neoliberalism-Thirdway_desc = American Thirdway description.
usantde_Neoliberalism-Thirdway_pics = game/hog/us/thirdway.png 6.4 Base ideology namespace
The shared base form has no national tag:
<IdeologyName>_gov = #HEX/Title
<IdeologyName>_desc = Description
<IdeologyName>_pics = game/hog/folder/image.png Example:
Neoliberalism-Thirdway_gov = #FFFFFF/Neoliberalism-Thirdway
Neoliberalism-Thirdway_desc = Shared base ideology description.
Neoliberalism-Thirdway_pics = game/hog/neoliberalism/thirdway.png The national and base forms are separate namespaces:
NationalTag_Ideology_* exists -> use the complete national namespace.
No national namespace exists -> use the complete base Ideology_* namespace. The game does not mix individual fields between namespaces. If the US namespace defines only _desc, it does not borrow _pics from the base namespace. Define all three national keys when the national version needs a title, picture, and description.
Namespace selection is implemented in:
HeadOfGovernmentSupport.buildIdeologyHover(int civId)
HeadOfGovernmentSupport.hasIdeologyNamespace(String keyBase) Selection order:
1. <RealNationalTag>_<IdeologyName>
2. <CurrentCivTag>_<IdeologyName>, when different from the real tag
3. <IdeologyName> base namespace This selects one complete namespace. It is not a per-field fallback for _gov, _desc, or _pics.
Render order:
Title from *_gov
Picture from *_pics
Description from *_desc If _pics is absent from the selected namespace, the description follows the title directly.
Do not use the literal word ideologies as a command. Do not use _decs. The valid suffix is _desc.
National tags, ideology names, folders, and filenames are case-sensitive.
7. Peace out with one country
7.1 Exact donor behavior
The outcome selects one civilization, then loops through every civilization and calls whitePeace() for each relation equal to -100.
Selected civilization -> ends every current war involving that civilization It does not limit the action to one selected pair, even though Event_Outcome_WhitePeace still contains an iCivID2 field.
7.2 Required classes
Event_Outcome_WhitePeace
Menu_CreateScenario_Events_Out_WhitePeace
Menu_CreateScenario_Events_AddNewOutcome
MenuManager 7.3 Outcome code
The action belongs in:
Lage/of/civilizations2/jakowski/lukasz/Event_Outcome_WhitePeace;->outcomeAction()V Core loop:
invoke-virtual {p0}, Lage/of/civilizations2/jakowski/lukasz/Event_Outcome_WhitePeace;->getCivID()I
move-result v0
const/4 v1, 0x0
:peace_loop
sget-object v2, Lage/of/civilizations2/jakowski/lukasz/CFG;->game:Lage/of/civilizations2/jakowski/lukasz/Game;
invoke-virtual {v2}, Lage/of/civilizations2/jakowski/lukasz/Game;->getCivsSize()I
move-result v2
if-ge v1, v2, :peace_done
if-eq v1, v0, :peace_next
sget-object v2, Lage/of/civilizations2/jakowski/lukasz/CFG;->game:Lage/of/civilizations2/jakowski/lukasz/Game;
invoke-virtual {v2, v0, v1}, Lage/of/civilizations2/jakowski/lukasz/Game;->getCivRelation_OfCivB(II)F
move-result v2
float-to-int v2, v2
const/16 v3, -0x64
if-ne v2, v3, :peace_next
sget-object v2, Lage/of/civilizations2/jakowski/lukasz/CFG;->game:Lage/of/civilizations2/jakowski/lukasz/Game;
invoke-virtual {v2, v0, v1}, Lage/of/civilizations2/jakowski/lukasz/Game;->whitePeace(II)V
:peace_next
add-int/lit8 v1, v1, 0x1
goto :peace_loop
:peace_done
return-void Labels must be unique inside the method.
8. Android native UI keyboard
This feature replaces the libGDX-drawn keyboard with an Android input bar and the system keyboard.
8.1 Import all seven classes
NativeKeyboardBridge
NativeKeyboardBridge$CommitText
NativeKeyboardBridge$KeyboardLayoutListener
NativeKeyboardBridge$SaveClick
NativeKeyboardBridge$SaveImeAction
NativeKeyboardBridge$ShowBar
NativeKeyboardBridge$ShowIme Import every class with the NativeKeyboardBridge$ prefix shown in the donor DEX.
8.2 Hook Keyboard.setVisible(Z)
Open:
Lage/of/civilizations2/jakowski/lukasz/Keyboard;->setVisible(Z)V Use:
.method protected final setVisible(Z)V
.registers 2
if-eqz p1, :hide_keyboard
invoke-static {}, Lage/of/civilizations2/jakowski/lukasz/NativeKeyboardBridge;->show()V
return-void
:hide_keyboard
invoke-super {p0, p1}, Lage/of/civilizations2/jakowski/lukasz/SliderMenu;->setVisible(Z)V
return-void
.end method The bridge reads:
Lage/of/civilizations2/jakowski/lukasz/CFG;->keyboardMessage:Ljava/lang/String; It obtains the Android Activity from libGDX and creates the UI on the main thread:
sget-object v0, Lcom/badlogic/gdx/Gdx;->app:Lcom/badlogic/gdx/Application;
check-cast v0, Landroid/app/Activity;
invoke-virtual {v0, v2}, Landroid/app/Activity;->runOnUiThread(Ljava/lang/Runnable;)V No res/layout file is required. The bridge creates LinearLayout, EditText, and GradientDrawable objects in code.
This implementation is Android-specific. Do not transplant it unchanged into a desktop launcher.
9. Separate Super Event renderer
The Super Event renderer is separate from the target’s normal event renderer.
9.1 Import the renderer classes
Menu_InGame_Event_Super
Menu_InGame_Event_Super$1
Menu_InGame_Event_Super$2 Omitting $1 or $2 can cause NoClassDefFoundError when an event opens.
9.2 Install the frame and pictures
Required frame:
assets/UI/events/super_event_completed_version.png The donor also includes:
assets/UI/events/9_11.png
assets/UI/events/9_11.png.ogg
assets/UI/events/default.png
assets/UI/events/event_frame_2000.png
assets/UI/events/pre911.png Picture lookup order:
assets/map/<active-map>/scenarios/<scenario-tag>/events/<picture-file>
assets/UI/events/<picture-file> If the event picture is empty, the renderer tries default.png.
Optional music uses the full picture filename plus .ogg:
9_11.png -> 9_11.png.ogg 9.3 Add the event flag
Add to Event_GameData:
.field private superEvent:Z Initialize it to false in the constructor:
const/4 v0, 0x0
iput-boolean v0, p0, Lage/of/civilizations2/jakowski/lukasz/Event_GameData;->superEvent:Z Add:
.method protected final getSuperEvent()Z
.locals 1
iget-boolean v0, p0, Lage/of/civilizations2/jakowski/lukasz/Event_GameData;->superEvent:Z
return v0
.end method
.method protected final setSuperEvent(Z)V
.locals 0
iput-boolean p1, p0, Lage/of/civilizations2/jakowski/lukasz/Event_GameData;->superEvent:Z
return-void
.end method 9.4 Route the event renderer
Merge the branch into:
Lage/of/civilizations2/jakowski/lukasz/MenuManager;->rebuildInGame_Event()V sget-object v6, Lage/of/civilizations2/jakowski/lukasz/CFG;->eventsManager:Lage/of/civilizations2/jakowski/lukasz/EventsManager;
sget v7, Lage/of/civilizations2/jakowski/lukasz/Menu_InGame_Event;->EVENT_ID:I
invoke-virtual {v6, v7}, Lage/of/civilizations2/jakowski/lukasz/EventsManager;->getEvent(I)Lage/of/civilizations2/jakowski/lukasz/Event_GameData;
move-result-object v6
invoke-virtual {v6}, Lage/of/civilizations2/jakowski/lukasz/Event_GameData;->getSuperEvent()Z
move-result v6
if-eqz v6, :use_normal_event
new-instance v6, Lage/of/civilizations2/jakowski/lukasz/Menu_InGame_Event_Super;
invoke-direct {v6}, Lage/of/civilizations2/jakowski/lukasz/Menu_InGame_Event_Super;-><init>()V
invoke-interface {v4, v5, v6}, Ljava/util/List;->set(ILjava/lang/Object;)Ljava/lang/Object;
goto :event_renderer_ready
:use_normal_event
new-instance v6, Lage/of/civilizations2/jakowski/lukasz/Menu_InGame_Event;
invoke-direct {v6}, Lage/of/civilizations2/jakowski/lukasz/Menu_InGame_Event;-><init>()V
invoke-interface {v4, v5, v6}, Ljava/util/List;->set(ILjava/lang/Object;)Ljava/lang/Object;
:event_renderer_ready Keep the target’s existing normal renderer. If it uses Menu_InGame_Event_Web2000, do not replace it with the stock Menu_InGame_Event class.
The method must have enough locals for v7. The Super branch must jump past the normal renderer’s resize block.
10. Normal Event and Super Event selection
10.1 Template label
Add to Event_GameData:
.method protected final getEventTemplateText()Ljava/lang/String;
.locals 1
iget-boolean v0, p0, Lage/of/civilizations2/jakowski/lukasz/Event_GameData;->superEvent:Z
if-eqz v0, :normal_template
const-string v0, "Event Template: Super Event"
return-object v0
:normal_template
const-string v0, "Event Template: Normal Event"
return-object v0
.end method 10.2 Event Editor button
Merge the template button into:
Menu_CreateScenario_Events_Edit Read the label from the active editor event:
sget-object v0, Lage/of/civilizations2/jakowski/lukasz/CFG;->eventsManager:Lage/of/civilizations2/jakowski/lukasz/EventsManager;
iget-object v0, v0, Lage/of/civilizations2/jakowski/lukasz/EventsManager;->lCreateScenario_Event:Lage/of/civilizations2/jakowski/lukasz/Event_GameData;
invoke-virtual {v0}, Lage/of/civilizations2/jakowski/lukasz/Event_GameData;->getEventTemplateText()Ljava/lang/String;
move-result-object v1 Toggle the Boolean when the button is pressed:
invoke-virtual {p1}, Lage/of/civilizations2/jakowski/lukasz/Event_GameData;->getSuperEvent()Z
move-result v0
xor-int/lit8 v0, v0, 0x1
invoke-virtual {p1, v0}, Lage/of/civilizations2/jakowski/lukasz/Event_GameData;->setSuperEvent(Z)V Refresh the button text with getEventTemplateText() afterward.
Adding an editor element changes action indexes. Update the fixed-element count, packed switch, decision-list subtraction, and refreshed menu-element ID. Do not copy packed-switch numbers from an editor with a different layout.
superEvent = false -> Normal Event
superEvent = true -> Super Event Old events default to Normal Event.
11. Unrestricted Trigger Another Event
This outcome lists every event in the scenario, stores the selected event tag, and runs it through runEvent_Tag().
11.1 Import the classes
Event_Outcome_TriggerAnotherEvent
Menu_CreateScenario_Events_Out_TriggerAnotherEvent
Menu_CreateScenario_Events_SelectEvent
Menu_CreateScenario_Events_SelectEvent$1
Menu_CreateScenario_Events_SelectEvent_List
Menu_CreateScenario_Events_SelectEvent_List$1 Merge:
Menu_CreateScenario_Events_AddNewOutcome
MenuManager 11.2 Run the event tag
Use in Event_Outcome_TriggerAnotherEvent.outcomeAction():
invoke-virtual {p0}, Lage/of/civilizations2/jakowski/lukasz/Event_Outcome_TriggerAnotherEvent;->canMakeAction()Z
move-result v0
if-eqz v0, :trigger_done
sget-object v0, Lage/of/civilizations2/jakowski/lukasz/CFG;->eventsManager:Lage/of/civilizations2/jakowski/lukasz/EventsManager;
invoke-virtual {p0}, Lage/of/civilizations2/jakowski/lukasz/Event_Outcome_TriggerAnotherEvent;->getText()Ljava/lang/String;
move-result-object v1
invoke-virtual {v0, v1}, Lage/of/civilizations2/jakowski/lukasz/EventsManager;->runEvent_Tag(Ljava/lang/String;)V
:trigger_done
return-void 11.3 List every event
Menu_CreateScenario_Events_SelectEvent_List must iterate over:
sget-object v3, Lage/of/civilizations2/jakowski/lukasz/CFG;->eventsManager:Lage/of/civilizations2/jakowski/lukasz/EventsManager;
invoke-virtual {v3}, Lage/of/civilizations2/jakowski/lukasz/EventsManager;->getEventsSize()I
move-result v3 When an item is selected, store the event tag rather than the display name:
invoke-virtual {v1}, Lage/of/civilizations2/jakowski/lukasz/Event_GameData;->getEventTag()Ljava/lang/String;
move-result-object v1
invoke-virtual {v0, v1}, Lage/of/civilizations2/jakowski/lukasz/Event_Outcome;->setText(Ljava/lang/String;)V The donor list displays:
Event name, Civilization name [EventTag] The event currently being edited should be non-clickable to prevent immediate self-triggering. Chains such as A -> B -> C are valid. Avoid circular chains such as A -> B -> A.
12. Add outcomes to Event Marker
New outcomes must be registered in:
Menu_CreateScenario_Events_AddNewOutcome Each new entry requires:
- A
MenuElementdisplaying the outcome name. - A matching case in
actionElement(I). - Creation of the correct
Event_Outcome_*object and navigation to its editor.
Examples:
new-instance v0, Lage/of/civilizations2/jakowski/lukasz/Event_Outcome_RenameCity;
invoke-direct {v0}, Lage/of/civilizations2/jakowski/lukasz/Event_Outcome_RenameCity;-><init>()V new-instance v0, Lage/of/civilizations2/jakowski/lukasz/Event_Outcome_WhitePeace;
invoke-direct {v0}, Lage/of/civilizations2/jakowski/lukasz/Event_Outcome_WhitePeace;-><init>()V new-instance v0, Lage/of/civilizations2/jakowski/lukasz/Event_Outcome_TriggerAnotherEvent;
invoke-direct {v0}, Lage/of/civilizations2/jakowski/lukasz/Event_Outcome_TriggerAnotherEvent;-><init>()V Head of Government uses Event_Outcome_Leader, but its editor and runtime behavior are routed through HeadOfGovernmentSupport.
When a new button changes an index, update the packed switch and every following index calculation. An incorrect index can open the wrong outcome or crash the editor.
13. Save, rebuild, and sign in MT Manager
- Save every modified class in Dex Editor++.
- Let MT Manager compile and replace
classes.dex. - Return to the APK archive.
- Verify every asset path and letter case.
- Exit the archive and choose Sign.
- Enable signature schemes v1 and v2. Enable v3 when supported.
- Save the signed APK under a new name.
Example:
/storage/emulated/0/LojiDexWork/target-loji-signed.apk If Android reports a signature conflict, back up the save data first. Then uninstall the old build, sign with the old keystore, or install a clone with a different package name.
14. Test order
Use a small test scenario and enable one feature group at a time.
[ ] The APK reaches the main menu
[ ] Scenario Editor opens
[ ] Event Editor opens
[ ] Rename City selects a province and city, then updates the name
[ ] HOG selection accepts Civilization, Ideology, and Leader
[ ] The new leader updates title, name, picture, and description
[ ] A .gif leader plays all frames rather than showing one frame
[ ] Ideology hover renders title -> picture -> description
[ ] National ideology content does not mix with base ideology fields
[ ] Base ideology content appears when no national namespace exists
[ ] White Peace ends the selected civilization's active wars
[ ] Android native keyboard accepts and saves text
[ ] Normal Events retain the original renderer
[ ] Super Events display the frame, picture, title, and decision
[ ] Normal/Super selection persists after reopening the editor
[ ] Trigger Another Event lists all events and runs the selected tag
[ ] A new save does not retain an old HOG override 15. Troubleshooting
The app does not start
Likely causes:
- A complete
MenuManagerwas copied from an incompatible engine. - An inner class such as
$1or$2is missing. - A class was imported into the wrong DEX.
- A method descriptor differs from the target.
- Smali uses an undeclared register.
- A label or packed-switch entry is invalid.
Restore the backup and port one feature group at a time.
NoClassDefFoundError
Import the missing helper or inner class named in the exception.
VerifyError when an outcome runs
Check .locals or .registers, object types after move-result-object, and the complete invoked method descriptor.
Ideology text appears without the picture
Verify:
assets/game/hog/<file>
<NationalTag>_<IdeologyName>_pics = game/hog/<file> For base ideology content:
<IdeologyName>_pics = game/hog/<file> Confirm which namespace is selected. Fields are not mixed between national and base namespaces.
The leader name changes but the picture or description does not
Check these hooks:
prepareLeaderFromSource
matchingLeaderBundleTagFromAllKeys
resolveGovernmentTitle
resolveName
resolvePicture
resolveDescription Also verify all four Bundle values:
*_gov
*_gov_leader
*_gov_leader_pic
*_gov_leader_desc The GIF loads but does not animate
Check:
GifLeaderSupport
GifLeaderSupport$GifState
CFG -> GifLeaderSupport.loadLeader()
Every leader renderer -> GifLeaderSupport.beforeDraw() The filename must end in .gif, and Android Movie.decodeStream() must accept the file.
Super Event uses Normal Event dimensions
The Super branch is falling through the normal renderer’s resize block. Jump to a label after the normal resize logic.
Trigger Another Event does not run
The outcome stores an event tag, not an event name. Confirm that the selector calls getEventTag() and the action calls runEvent_Tag().
The keyboard opens but Save does nothing
Confirm that SaveClick, SaveImeAction, and CommitText were imported. Check CFG.keyboardMessage and the target’s original keyboard action.
16. Smali porting rules
- Back up every existing class before overwriting it.
- Import central classes unchanged only when donor and target use the same base.
- Merge small changes into
CFG,MenuManager, andEventsManager. - Do not assume menu IDs are identical across mods.
- Never reduce an existing
.localsvalue. - Labels must be unique inside their method.
- Build, sign, and launch the APK after each feature group.
- Asset paths are case-sensitive and have no leading slash inside the APK.
17. Path reference
Game code:
classes.dex
age/of/civilizations2/jakowski/lukasz/
Bundle:
assets/game/languages/Bundle.properties
Ideology pictures:
assets/game/hog/
Leader pictures and GIF files:
assets/game/leadersIMG/
Super Event frame and global pictures:
assets/UI/events/
Scenario-specific event pictures:
assets/map/<map>/scenarios/<scenario-tag>/events/ The donor classes2.dex is an MT Manager file-integration component. It is not required for the game features in this guide.
Дополнительная информация
Название мода: Loji classes.dex
Разработчик: Workerofisland094
Оф. сайт: Discord
Движок (игра): Loji DEX (Age of History 2)
Язык: английский
Дата выхода: 16 июля 2026 г.
Обновлено: 15 сентября 2026 г.
Примечание:
1) Все могут свободно использовать файл classes.dex! Если вы обнаружите какие-либо ошибки, пожалуйста, сообщите о них в Discord @Workerofisland094 .
2) Инструкция по установке в свой мод (андроид) есть в архиве.
3) Также для обычных пользователей сделан мод The Bozo Rises с кодом Loji DEX.

