Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions LICENSE-ROXAS2712.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Roxas2712 — Fork Contributions

Version 1.0 · Effective 25 October 2026

Copyright © 2026 Roxas2712, to the extent of rights held.
All rights reserved except as expressly granted below or already granted.

## 1. Scope and ownership

This notice applies only to original contributions made by Roxas2712
(GitHub: Roxas2712) to `Roxas2712/Gen2Recomped`, including original additions
within modified files, insofar as he owns the relevant rights. Git history,
pull-request diffs and source provenance identify contributions; a commit
author or a whole-file change alone does not establish ownership of every
part of that file.

It does not claim ownership of Cedric's work, Gen1Recomp / BOIS CLUB GAMES
work, other contributors' work, third-party dependencies, or ROM-derived
content. Their applicable licences and notices remain in force. It does not
grant permission to distribute the complete combined fork where another
rights holder's permission is required.

## 2. General permission and reserved rights

For contributions covered by this notice and not already licensed more
broadly, you may read and study the published source and make private,
non-commercial modifications and builds for your own use. This grants no
general permission to redistribute, sublicense, sell, or offer those
contributions as a hosted service. Other uses require Roxas2712's
written permission, except for the specific permission below and rights
available independently under applicable law.

Keep the relevant copyright, licence and attribution notices. Do not
represent Roxas2712's contributions as exclusively authored by someone
else. Public source availability alone does not put them in the public
domain or grant an open-source licence.

## 3. Specific permission for Cedric / UNDERdecoded

Roxas2712 expressly grants Cedric (Ced; GitHub: UNDERdecoded /
UNDERdecodedHD) a non-exclusive, worldwide, perpetual, royalty-free
permission to use, reproduce, integrate, modify and distribute the covered
contributions that Roxas2712 submits or expressly offers through pull
requests to `UNDERdecoded/Gen2Recomped`, for Cedric's Gen2Recomped /
RetroRecompiled project. This includes its renamed continuation maintained
by Cedric, its official builds, and its commercial operation.

Cedric may license the incorporated contributions to recipients as part of
that project, subject to preserving this permission's applicable credit and
notice requirements. This permission is not a copyright assignment, is not
exclusive, and does not cover unpublished work, separate repositories,
ModCompass service operation, credentials, or content not offered in those
pull requests. Roxas2712 retains his own rights to his contributions.

For uses relying on this permission:

- retain the relevant source copyright and licence notices;
- give visible credit in the project's README or equivalent public project
page, and in the application's existing About / Credits view, if present;
- use at least the following credit, or a substantially equivalent one:

> Contributions by Roxas2712 —
> https://github.com/Roxas2712

A maintained consolidated contributor credit is sufficient; a separate
credit on every screen or for every change is not required. Adapted
contributions must not be attributed exclusively to another author. Other
contributors' credits must also remain intact.

## 4. Earlier licences and permissions remain effective

This notice does not revoke, narrow, or retrospectively add conditions to
any permission already validly granted, including MIT / GPL grants,
separate agreements, or permissions arising from previously submitted
pull requests. Where an earlier grant permits a use independently of this
notice, its existing terms govern that use. For such earlier uses, the
visible credit above is requested to the extent it is not already required.

For subsequent contributions, this is the default statement of Roxas2712's own reserved rights and the specific PR permission, subject to any
applicable existing licence obligations or separately agreed terms. It
does not change another rights holder's licence or guarantee compatibility
of a particular combination of code.

## 5. No warranty

To the extent permitted by applicable law, these contributions are provided
as is, without warranty. No support obligation, endorsement, or rights to
third-party code, game assets, names, or trademarks are granted.
31 changes: 31 additions & 0 deletions docs/native-companion-terrain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Native companion terrain observations

Contributions by Roxas2712 — <https://github.com/Roxas2712>.
Existing upstream rights and notices remain applicable to the shared native machinery.

The GBA source namespace now reads grass, next elevation and ledge landing
from the native map owner. Elevation zero remains a real height; bridge
markers preserve the incoming height. Native movement and source queries
share the same transition and ledge classifier. Dataset overrides, older
standing-cell ledges, later-edition jump distances and callback arity remain
native. Queries never move actors, execute movement hooks or trigger warps.

`src.core.game3.encounters` provides `ensureLoaded`, `terrainAt` and `tableFor`
(`table_for` alias). Tables expose detached land/water/rocks/fishing areas,
with cartridge species indices and source level-range field names. Map
aliases resolve through the checked native map binding. Native header and
script-variable changes are visible on the next query; unknown species or
retired maps are refused instead of silently substituting another monster.
Altering Cave header selection is shared with native encounter generation.
These reads do not consume RNG or install another import cache.

This is a partial interface. Encounter rolling, encounter identity transfer
into battles, companion actor collection, follower commands and UI hooks
still need their own integrations. No complete companion or presentation
capability version is advertised, and unchanged Origami 1.0.9 initialization
is not yet expected to succeed.

Validation uses synthetic native owners for all five GBA editions, actual
native hop dispatch, height/bridge transitions, cave/underwater terrain,
sparse species indices, alternate headers, retired bindings, detached
records and real SDK failure/teardown alongside a native peer.
52 changes: 52 additions & 0 deletions docs/native-field-collision.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Compatibility collision reads and native entry permission

The collision module reads the live native GBA session. Classified COLL bytes
remain detached source-format observations, while `canEnter` calls the existing
native permission owner. No collision grid, actor state, warp event, script or
save is changed by a query. Hypothetical origins, elevations and surf states
use an observational mover; its real native actor and connected-map aliases
are excluded from self-collision.

Source behavior comes from original bank attributes and the existing semantic
translator, including neighboring banks and authored borders. The existing
map graph determines which metatile/pair a world coordinate samples. Native
grid revisions and source-bank replacements remain visible. Classified arrays
are cached by owned layout/revision; mutating such an observation cannot
change native entry permission. Native extents remain authoritative for entry
and bounds even where the source art layout has padding.

The module exposes current-map/behavior/elevation/cell/grid observations,
native walkable/water queries, source-format active warp observations and
`canEnter`. Header-only warps on non-warp terrain stay inactive. Loader teardown
retires readers and arrays after wrapper unwind. Held detached cells do not
retain native maps or source banks. Sessions whose world belongs to an earlier
dataset cannot grant movement.

## Verification and integration

`luajit tools/field-collision/check.lua` runs 486 ROM-free checks across all five
editions using real native map and collision implementations. It covers native
permission/reason agreement, source-bank semantics, connected/border sampling,
grid changes, hypothetical requests, active/dead warps, detached overrides,
dataset replacement, real loader teardown and weak-reference retirement.
The player phase's 261 checks also pass with this session guard.

The composed fork's full unchanged 1.0.9 source loader probe now reaches the
missing `src.core.game3.tileset_native` module for all five editions. Full
initialization, GPU rendering and gameplay remain unaccepted. These probes
use fixture game data and read the full source package without modifying it.

The standalone upstream diff requires player/session PR #254 and map-read
PR #251 with its terrain/identity dependencies. All-five testing also requires
cartridge PRs #183, #186 and #187. Its focused fixture runs in the composed
fork; it cannot independently exercise missing prerequisite interfaces on
pristine upstream main.

## Remaining work

Custom source-authored collision providers, binding/loading a different map,
typed script writes and the rest of the source collision/field service
surface still need integration. They must enter native commands rather than
turning the detached presentation grid into a second simulation. The atlas,
distinct session facade, field/UI/battle services and end-to-end acceptance
remain separate workstreams.
94 changes: 94 additions & 0 deletions src/compat/gen1/CollisionRuntime.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
-- Source observations and native permission queries have different alphabets:
-- classified COLL bytes are views; actual entry permission stays native.
local Session=require('src.compat.gen1.NativeSession')
local Map=require('src.core.game3.map')
local Tiles=require('src.render.Gen3Tiles')
local Behavior=require('src.compat.gen1.MetatileBehavior')
local Version=require('src.core.GameVersion')
local Native=require('src.world.Collision')
local M={}
local function integer(v)return type(v)=='number'and v==v and math.abs(v)<math.huge and v%1==0 end
local directions={up=true,down=true,left=true,right=true}
function M.new()
local readers=setmetatable({},{__mode='k'})
local grids=setmetatable({},{__mode='k'})
local runtime={}
local function context(game)
local g,w,p=Session.resolve(game);if not g then return nil,w end
local maps,why=Map._context(g);if not maps then return nil,why end
return g,w,p,maps
end
function runtime:current(game)
local g,_,_,maps=context(game);if not g then return nil end
return maps:current()
end
function runtime:behaviorMid(def,mid,game)
if not(integer(mid)and mid>=0 and mid<1024)then return nil end
local g,_,_,maps=context(game);if not g or not maps:isOwned(def)then return nil end
local native=g.data.maps[def.nativeId]
local pair=native and g.data.tilesets[native.tileset]
local sets=g.data.map_tilesets
local a=pair and sets and sets[pair.primaryKey]
local b=pair and sets and sets[pair.secondaryKey]
local geometry=g.data.constants and g.data.constants.gen3Layout
if not(a and b and geometry)then return nil end
local cached=readers[pair]
if not cached or cached.primary~=a or cached.secondary~=b or cached.geometry~=geometry
or cached.split~=geometry.metatilesInPrimary then
cached={primary=a,secondary=b,geometry=geometry,split=geometry.metatilesInPrimary,
reader=Tiles.new({primary=a,secondary=b},geometry)}
readers[pair]=cached
end
local raw=cached.reader:sourceBehavior(mid)
return raw~=nil and Behavior.fromRaw(Version.get(),raw)or nil
end
function runtime:behavior(def,x,y,game)
if not(integer(x)and integer(y))then return nil end
local layout=def and def.midLayout
if not layout or x<0 or y<0 or x>=layout.width or y>=layout.height then return nil end
return self:behaviorMid(def,layout:midAt(x,y),game)
end
function runtime:grid(game)
local def=self:current(game);local layout=def and def.midLayout
if not layout then return nil end
local cached=grids[layout]
if not cached or cached.revision~=layout._revision then
cached={revision=layout._revision,cells=layout:collArray()};grids[layout]=cached
end
return cached.cells
end
function runtime:canEnter(game,x,y,opts)
if not(integer(x)and integer(y))then return false,'bounds'end
local g,w,p=Session.resolve(game);if not g then return false,'bounds'end
opts=opts or {}
local fx,fy=opts.fromX or p.cellX,opts.fromY or p.cellY
if not(integer(fx)and integer(fy))then return false,'bounds'end
local direction=opts.dir
if direction~=nil and not directions[direction]then return false,'direction'end
if not direction then
if fx==x and fy~=y then direction=y>fy and'down'or'up'
elseif fy==y and fx~=x then direction=x>fx and'right'or'left'end
end
local elevation=opts.elevation
if elevation==nil then elevation=p.elevation end
local surfing=opts.surfing
if surfing==nil then surfing=p.surfing end
local cast=w.cast or w.entities or {}
local mover=p
if fx~=p.cellX or fy~=p.cellY or elevation~=p.elevation or surfing~=p.surfing then
mover={};for k,v in pairs(p)do mover[k]=v end
mover.cellX,mover.cellY,mover.elevation,mover.surfing=fx,fy,elevation,surfing
local filtered={}
for _,entity in ipairs(cast)do
if entity~=p and entity.of~=p then filtered[#filtered+1]=entity end
end
cast=filtered
end
return Native.mayEnter(w.map,cast,mover,x,y,direction)
end
function runtime:release()
readers=setmetatable({},{__mode='k'});grids=setmetatable({},{__mode='k'})
end
return runtime
end
return M
Loading