Skip to content

Commit 6ec232c

Browse files
committed
GBA: Klonoa - Empire of Dreams
Started Klonoa Module
1 parent a3fab6e commit 6ec232c

2 files changed

Lines changed: 231 additions & 0 deletions

File tree

ScriptHawk.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,9 @@ local supportedGames = {
484484
["AF51AB03A173DEC28C9241532227CD64"] = {moduleName="games.impossible_mission", friendlyName="Impossible Mission (E)"},
485485
["A26D40B6B7646C22D1F2DB7F746F0391"] = {moduleName="games.impossible_mission", friendlyName="Impossible Mission (E) (Beta)"},
486486

487+
--Klonoa: Empire of Dreams
488+
["A0A298D9DBA1BA15D04A42FC2EB35893D1A9569B"] = {moduleName="games.GBA_klonoa", friendlyName="Klonoa - Empire of Dreams (USA)"},
489+
487490
-- Land of Illusion
488491
["07FAC1D61BC20CF6EB298F66EC2FFE49"] = {moduleName="games.land_of_illusion", friendlyName="Land of Illusion Starring Mickey Mouse (E)"},
489492

games/GBA_klonoa.lua

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
if type(ScriptHawk) ~= "table" then -- An error message to inform the user that this is a game module, not a standalone script
2+
print("This script is not designed to run by itself");
3+
print("Please run ScriptHawk.lua from the parent directory instead");
4+
print("Thanks for using ScriptHawk :)");
5+
return;
6+
end
7+
8+
local Game = {
9+
Memory = {
10+
obj_array_base = {Domain="IWRAM", Address=0x2920},
11+
obj_array_size = {Domain="IWRAM", Address=0x5428},
12+
grabbed_obj_index = {Domain="IWRAM", Address=0x5262}
13+
},
14+
speedy_speeds = { .001, .01, .1, 1, 5, 10, 20, 50, 100 }, -- D-Pad speeds, scale these appropriately with your game's coordinate system
15+
speedy_index = 7, -- Default speed, index into the speedy_speeds table
16+
};
17+
18+
--------------------
19+
-- Region/Version --
20+
--------------------
21+
local obj_struct_size = 0x1C;
22+
local obj_struct = {
23+
[0x00] = {type="s16_le", name="XPosition"},
24+
[0x02] = {type="s16_le", name="YPosition"},
25+
[0x04] = {type="s16_le", name="XScreenOffset"},
26+
[0x06] = {type="s16_le", name="YScreenOffset"},
27+
[0x08] = {type="u8_le", name="InvincTimer"},
28+
};
29+
30+
function Game.detectVersion(romName, romHash)
31+
ScriptHawk.dpad.joypad.enabled = false;
32+
ScriptHawk.dpad.key.enabled = false;
33+
return true;
34+
end
35+
36+
local function parsePointer(inPtr)
37+
if (inPtr >= 0x02000000) and (inPtr < 0x02040000) then
38+
return {
39+
Domain = "EWRAM",
40+
Address = inPtr - 0x02000000,
41+
};
42+
elseif (inPtr>= 0x03000000) and (inPtr < 0x03008000) then
43+
return {
44+
Domain = "IWRAM",
45+
Address = inPtr - 0x03000000,
46+
};
47+
end
48+
end
49+
50+
function dereferencePointer(inPtr)
51+
return parsePointer(memory.read_u32_le(inPtr.Address, inPtr.Domain));
52+
end
53+
54+
55+
-------------------
56+
-- Physics/Scale --
57+
-------------------
58+
59+
-- Optional: If lag in your game is more complicated than a simple emu.islagged() call you should add the logic to detect it here
60+
function Game.isPhysicsFrame()
61+
-- Implementing this logic will result in smooth dY/dXZ calculation (no more flickering between 0 and the correct value)
62+
return not emu.islagged();
63+
end
64+
65+
--------------
66+
-- Objects --
67+
--------------
68+
function Game.getObjXPos(obj_index)
69+
local objArraySize = memory.read_u8(Game.Memory.obj_array_size.Address, Game.Memory.obj_array_size.Domain);
70+
if obj_index<objArraySize then
71+
local objAddr = {Address = Game.Memory.obj_array_base.Address,
72+
Domain = Game.Memory.obj_array_base.Domain
73+
};
74+
objAddr.Address = objAddr.Address + (obj_index * obj_struct_size);
75+
return memory.read_s16_le(objAddr.Address, objAddr.Domain);
76+
else
77+
return 0;
78+
end
79+
end
80+
81+
function Game.getObjYPos(obj_index)
82+
local objArraySize = memory.read_u8(Game.Memory.obj_array_size.Address, Game.Memory.obj_array_size.Domain);
83+
if obj_index<objArraySize then
84+
local objAddr = {Address = Game.Memory.obj_array_base.Address,
85+
Domain = Game.Memory.obj_array_base.Domain
86+
};
87+
objAddr.Address = objAddr.Address + (obj_index * obj_struct_size);
88+
return memory.read_s16_le(objAddr.Address + 0x02, objAddr.Domain);
89+
else
90+
return 0;
91+
end
92+
end
93+
94+
function Game.getObjXOffset(obj_index)
95+
local objArraySize = memory.read_u8(Game.Memory.obj_array_size.Address, Game.Memory.obj_array_size.Domain);
96+
if obj_index<objArraySize then
97+
local objAddr = {Address = Game.Memory.obj_array_base.Address,
98+
Domain = Game.Memory.obj_array_base.Domain
99+
};
100+
objAddr.Address = objAddr.Address + (obj_index * obj_struct_size);
101+
return memory.read_s16_le(objAddr.Address + 0x04, objAddr.Domain);
102+
else
103+
return 0;
104+
end
105+
end
106+
107+
function Game.getObjYOffset(obj_index)
108+
local objArraySize = memory.read_u8(Game.Memory.obj_array_size.Address, Game.Memory.obj_array_size.Domain);
109+
if obj_index<objArraySize then
110+
local objAddr = {Address = Game.Memory.obj_array_base.Address,
111+
Domain = Game.Memory.obj_array_base.Domain
112+
};
113+
objAddr.Address = objAddr.Address + (obj_index * obj_struct_size);
114+
return memory.read_s16_le(objAddr.Address + 0x06, objAddr.Domain);
115+
else
116+
return 0;
117+
end
118+
end
119+
120+
function Game.setObjXPos(obj_index, value)
121+
local objArraySize = memory.read_u8(Game.Memory.obj_array_size.Address, Game.Memory.obj_array_size.Domain);
122+
if obj_index<objArraySize then
123+
local objAddr = {Address = Game.Memory.obj_array_base.Address,
124+
Domain = Game.Memory.obj_array_base.Domain
125+
};
126+
objAddr.Address = objAddr.Address + (obj_index * obj_struct_size);
127+
return memory.write_s16_le(objAddr.Address, value, objAddr.Domain);
128+
else
129+
return 0;
130+
end
131+
end
132+
133+
function Game.setObjYPos(obj_index, value)
134+
local objArraySize = memory.read_u8(Game.Memory.obj_array_size.Address, Game.Memory.obj_array_size.Domain);
135+
if obj_index<objArraySize then
136+
local objAddr = {Address = Game.Memory.obj_array_base.Address,
137+
Domain = Game.Memory.obj_array_base.Domain
138+
};
139+
objAddr.Address = objAddr.Address + (obj_index * obj_struct_size);
140+
return memory.read_s16_le(objAddr.Address + 0x04, value, objAddr.Domain);
141+
else
142+
return 0;
143+
end
144+
end
145+
146+
--------------
147+
-- Position --
148+
--------------
149+
function Game.getXPosition()
150+
return Game.getObjXPos(0);
151+
end
152+
153+
function Game.getYPosition()
154+
return Game.getObjYPos(0);
155+
end
156+
157+
function Game.setXPosition(value)
158+
Game.setObjXPos(0, value);
159+
end
160+
161+
function Game.setYPosition(value)
162+
Game.setObjYPos(0, value);
163+
end
164+
165+
166+
function Game.drawObjectPositions()
167+
local objArraySize = memory.read_u8(Game.Memory.obj_array_size.Address, Game.Memory.obj_array_size.Domain);
168+
if objArraySize ~= 0 then
169+
for i = 0, objArraySize-1 do
170+
local xOffset = Game.getObjXOffset(i);
171+
local yOffset = Game.getObjYOffset(i);
172+
173+
if xOffset >= 0 and xOffset < 240 then
174+
if yOffset >= 0 and yOffset < 160 then
175+
gui.drawLine(xOffset, yOffset-2, xOffset, yOffset+2);
176+
gui.drawLine(xOffset-2, yOffset, xOffset+2, yOffset);
177+
gui.drawText(xOffset, yOffset, toHexString(i), null, null, 9);
178+
end
179+
end
180+
end
181+
end
182+
end
183+
184+
------------
185+
-- Events --
186+
------------
187+
188+
function Game.buttonHandler()
189+
print("Example button was pressed!");
190+
end
191+
192+
local labelValue = 0;
193+
function Game.initUI() -- Optional: Init any UI state here, mainly useful for setting up your form controls. Runs once at startup after successful version detection.
194+
-- Here are some examples for the most common UI control types
195+
ScriptHawk.UI.form_controls["Example Dropdown"] = forms.dropdown(ScriptHawk.UI.options_form, {"Option 1", "Option 2", "Option 3"}, ScriptHawk.UI.col(0) + ScriptHawk.UI.dropdown_offset, ScriptHawk.UI.row(7) + ScriptHawk.UI.dropdown_offset, ScriptHawk.UI.col(9) + 7, ScriptHawk.UI.button_height);
196+
ScriptHawk.UI.button(10, 7, {59}, nil, "Example Button", "Label", Game.buttonHandler);
197+
ScriptHawk.UI.button({13, -7}, 6, {ScriptHawk.UI.button_height}, nil, "Example Plus Button", "-", function() labelValue = labelValue + 1 end);
198+
ScriptHawk.UI.button({13, ScriptHawk.UI.button_height - 7}, 6, {ScriptHawk.UI.button_height}, nil, "Example Minus Button", "+", function() labelValue = labelValue - 1 end);
199+
ScriptHawk.UI.form_controls["Example Value Label"] = forms.label(ScriptHawk.UI.options_form, "0", ScriptHawk.UI.col(13) + ScriptHawk.UI.button_height + 21, ScriptHawk.UI.row(6) + ScriptHawk.UI.label_offset, 54, 14);
200+
ScriptHawk.UI.checkbox(10, 6, "Example Checkbox", "Label");
201+
end
202+
203+
-- Optional: This function should be used to draw to the screen or update form controls
204+
-- When emulation is running it will be called once per frame
205+
-- When emulation is paused it will be called as fast as possible
206+
--function Game.drawUI()
207+
-- forms.settext(ScriptHawk.UI.form_controls["Example Value Label"], labelValue);
208+
--end
209+
210+
211+
function Game.eachFrame()
212+
Game.drawObjectPositions();
213+
end
214+
215+
Game.OSD = {
216+
{"X", category="position"},
217+
{"Y", category="position"},
218+
{"Separator"},
219+
{"dY", category="positionStats"},
220+
{"dXZ", category="positionStats"},
221+
{"Separator"},
222+
{"Max dY", category="positionStatsMore"},
223+
{"Max dXZ", category="positionStatsMore"},
224+
{"Odometer", category="positionStatsMore"},
225+
{"Separator"}
226+
};
227+
228+
return Game; -- Return your Game table to ScriptHawk

0 commit comments

Comments
 (0)