Loji DEX

Age of History 2

Описание мода «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:

  1. Rename City.
  2. Update Leader and Head of Government.
  3. Animated GIF leaders.
  4. National and base ideology titles, descriptions, and pictures.
  5. Peace out with one country.
  6. Android native UI keyboard.
  7. Separate Super Event renderer.
  8. Normal Event and Super Event selection.
  9. 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:

  1. Open target.apk in the left panel.
  2. Open donor.apk in the right panel.
  3. Open classes.dex and choose Dex Editor++.
  4. 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.

  1. Extract classes.dex from donor.apk.
  2. Open target.apk as an archive.
  3. Replace the target classes.dex.
  4. Copy the required assets listed in this guide.
  5. 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.

  1. Extract the donor classes.dex.
  2. Open the target classes.dex in Dex Editor++.
  3. Choose Add, Import classes from dex, or the equivalent command.
  4. Select the donor DEX.
  5. Import new classes.
  6. 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

FeatureNew or primary classesExisting classes that require merging
Rename CityEvent_Outcome_RenameCity, Menu_CreateScenario_Events_Out_RenameCity, Menu_CreateScenario_Events_SelectCityMenu_CreateScenario_Events_AddNewOutcome, EventsManager, MenuManager
Head of GovernmentHeadOfGovernmentSupportEvent_Outcome_Leader, leader selection menus, EventsManager, MenuManager, Menu_InGame_CivInfo$2, Ideologies_Manager, Event_Outcome_ChangeIdeology
Animated GIF leaderGifLeaderSupport, GifLeaderSupport$GifStateCFG, Menu_InGame_CivInfo_Stats, Menu_Civilization_Info_Stats, MenuElement_Hover_v2_Element_Type_Leader
Ideology contentHeadOfGovernmentSupportIdeologies_Manager, Bundle.properties, assets/game/hog/
White PeaceEvent_Outcome_WhitePeace, Menu_CreateScenario_Events_Out_WhitePeaceMenu_CreateScenario_Events_AddNewOutcome, MenuManager
Native keyboardNativeKeyboardBridge and its six inner classesKeyboard.setVisible(Z)
Separate Super EventMenu_InGame_Event_Super, $1, $2Event_GameData, MenuManager, assets/UI/events/
Normal/Super pickeruses the Super Event classesEvent_GameData, Menu_CreateScenario_Events_Edit, MenuManager
Trigger Another Eventtrigger outcome and event selection menusMenu_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 .gif to 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 an assets/ 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:

  1. A MenuElement displaying the outcome name.
  2. A matching case in actionElement(I).
  3. 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

  1. Save every modified class in Dex Editor++.
  2. Let MT Manager compile and replace classes.dex.
  3. Return to the APK archive.
  4. Verify every asset path and letter case.
  5. Exit the archive and choose Sign.
  6. Enable signature schemes v1 and v2. Enable v3 when supported.
  7. 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 MenuManager was copied from an incompatible engine.
  • An inner class such as $1 or $2 is 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

  1. Back up every existing class before overwriting it.
  2. Import central classes unchanged only when donor and target use the same base.
  3. Merge small changes into CFG, MenuManager, and EventsManager.
  4. Do not assume menu IDs are identical across mods.
  5. Never reduce an existing .locals value.
  6. Labels must be unique inside their method.
  7. Build, sign, and launch the APK after each feature group.
  8. 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.

MODS.SU
Комментарии: 0