• Welcome to Final Fantasy Hacktics. Please login or sign up.
 
April 18, 2024, 01:58:51 pm

News:

Don't be hasty to start your own mod; all our FFT modding projects are greatly understaffed! Find out how you can help in the Recruitment section or our Discord!


Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Topics - Raijinili

1
Who is this for?

Python programmers who want to make a lot of repetitive changes, or want to help make repetitive changes. However, anyone who wants to learn Python can use this as a goal in mind.


What is this?

This intro will look a bit jargony, but please bear with me.

FFTPatcher is written in C#, compiled for .NET. What that means for us is, you can use its internal code in other Microsoft CLR languages.

Python is known as a popular beginner programming language, but also powerful enough to continue using for professional programmers, especially in scientific and numerical analysis.

IronPython is an interpreter for the Python programming language which runs on CLR. That means an IronPython program can load code in from FFTPatcher, and then execute it. Instead of navigating through the program itself, you write code, and then run it to make or change a patch.


What can be done with this?

Fundamentally, anything that FFTPatcher Suite can do, IronPython can do, but harder!

Programming has a few advantages over doing things manually:
- Repetitive work. A program doesn't get bored, so you could, for example, increase all monster Moves by 1 with just a few lines, rather than clicking through dozens of monsters.
- All your mistakes in one place! Instead of making a single typo in a file, you can make a typo in a script, and that typo will be repeated in all the files.
- Make your mod as a script. You can see all your changes in a single text file (or multiple files, if you choose to organize it that way), instead of clicking through tabs to find all changes you've made.
- Automated checks. You can make a script that ensures all gear have a reasonable cost for its power (as defined by you).
- More-direct manipulation. I believe it is possible to write a script that merges two existing FFTPatches and spits out another.
- Composability. Merging two scripts is probably easier than merging two of your patches manually. You can also break your mod up into multiple parts BECAUSE you know how easy it is to combine them.
- Reusable components. Load a library and tell it to replace all the "Dragon"s in the game to "Derpon"s.
- Version control! This topic spans many books, but think of it as branching savestates for your work. FFTPatcher creates patches in XML, but they're compressed and don't play well with version control. Version control isn't just for programmers. Here's an article for writers on using Git.


How do I start?

First, download FFTPatcher. Currently, we require version .493 or higher, because there were significant changes in the internals of the program, and we're playing with its internals.

Second, download the latest IronPython here. Unfortunately, as of this writing, IronPython hasn't caught up with Python 3, so we're stuck using Python 2.


Third, ??? <to be written>

Finally, join us on the FFH Discord, in the #fftpython channel. This is an educational channel, so make sure to stick to the topic.
2
New Project Ideas / Concept: Personality rewrites
August 29, 2020, 09:19:51 pm
(Elric has granted me an exception to the rule about "ideas you don't intend to personally pursue".)

Concept: A mod that changes the dialogue, but leaves everything else (including cutscene gestures) unchanged. Each character will be given a very different personality and goals. However, they maintain their jobs in the story, and the dialogue exchanges still happen in the same order (Ramza, then Delita, Algus, Delita). You can think of it as a translation patch by someone who doesn't read the language.

There can be several different mods with this idea. In this topic, we will take a comedic approach, but a dramatic rewrite can also work.

Example of what could be:

Take the following personalities.
* Ramza: Zapp Brannigan. A narcissistic blowhard, oblivious to much of the world that doesn't involve him.
* Agrias: Asshole Knight. Either that strict Paladin, or a straight-up asshole.
* Gafgarion: An old man who is too tired to argue. Resigned to go with the flow.

Orbonne opening battle:
Gafgarion: "Alright, folks, let's calm down here."
Agrias: "Kill them all!"
Gafgarion: "What? Why?"
Agrias: "I'm attacking!"

Gafgarion at the river: "Ramza, let's join up with these knights helping Princess Ovelia."
Ramza: "They must've brainwashed Gafgarion!"
Gafgarion: "What?"
Ramza: "Attack!"


Process

1. Stripping all of the text from the game, playing it through, and writing down your imagined interpretations.

2. Build by committee (on the forum): Go scene by scene, deciding character personalities and then writing out lines.

3. Each character is played by a poster or posters, and they roleplay their dialogue as the game progresses.

3
Patches here. Pull request made.

Four hacks:
  • Un-Truth uses PA instead of MA. (It's still magic damage.)
  • Truth, Un-Truth, and Holy Bracelet always do max number of hits.
  • Un-Truth treats Faith/Innocent as min/max instead of max/min. (Un-Truth normally deals full damage with Faith status.)
  • Truth spells are affected by Faith. (Evil hack: jumps into the middle of Faith spell damage calculation.)
4
New Project Ideas / FFT custom AI scripting concept
March 26, 2017, 11:55:40 pm
I want to externalize the AI. "Externalizing" meaning, when a character on a particular team is to take a turn, the emulator pauses, calls an external script (one per team) to decide what to do, writes the choices into memory, jumps to the FFT code, and resumes. The eventual goal is to have custom AI tournaments.

I ignore the problem of finding an emulator that can do this, and currently must figure out:
- Where to stop. (Entry point of turn-decider.)
- What data to expose to the script.
- Where to write to.
- Where to resume. (Exit point of decider.)

(For complete automation, there needs to be a hack to make "Crystal" automatically pick "Heal HP/MP", but I'm also ignoring that for now.)

Rather than running ASM within the emulator, I imagine the custom AI code running externally, and communicating with the emulator. This method doesn't need a way to generate ASM and find a place for it in the code, or for the scripter to write ASM. I'd aim to have Python bindings for ease of learning, and Lua bindings, since Lua is popular among emulators and the TAS community. The engine will probably be written in Lua, because, again, emulators commonly support Lua.
5
Input: n, an integer
Output: Random integer between 1 and n, inclusive. The results will tend toward the center of the distribution, symmetrically.

Python version:
Code (python) Select
from random import random as rand
def triangular_rand(n):
    return int((rand() + rand()) * n / 2) + 1

def triangular_rand(r4):
    stack -= 8
    var n
    var x
    r2 = rand()
    ^n = r4
    r2 = rand()
    ^x = result
    x = x + result
    LO, HI = x * n
    ^pop n
    r2 = x >> 0x10
    stack -= 8
    pop x
    return r2
    ^r2 = r2 + 1

Code (python) Select
from random import random as rand
def triangle_rand(n):
    lo = n // 2
    hi = (n + 1) // 2
    return int(rand()*lo + rand()*hi + 1)



Testing that the distribution is correct:
Code (python) Select

from collections import Counter
def test(n, times=100000):
    print(sorted(Counter(triangle_rand(n) for i in range(times)).items()))

test(5)
test(6)


I got it down to 20 15 lines.
ASM pseudocode ("^" indicates delay slot or multiplication buffer slot.):
def triangular_rand(r4):
    stack -= 8
    var n
    var x
    r2 = rand()
    ^n = r4
    r2 = rand()
    ^x = result
    x = x + result
    LO, HI = x * n
    ^pop n
    r2 = x >> 0x10
    stack -= 8
    pop x
    return r2
    ^r2 = r2 + 1


def triangle_rand(r4):
    stack -= 8
    push lo
    push hi
    lo = r4 >> 1
    r2 = rand()
    ^hi = r4 + 1
    MLO = r2 * lo
    ^hi = hi >> 1
    r2 = rand()
    ^lo = MLO
    MLO = r2 * hi
    nop
    hi = MLO
    r2 = lo + hi
    pop hi
    pop lo
    r2 = r2 >> 0x0f
    r2 = r2 + 1
    return r2
    ^stack += 8


It might be a good idea to r4 = r4 & 0xFF, to prevent any risk of overflow.
6
I played around with job unlocks and genders in ENTD. Here's what I found.

Generated battle units:

  • Starting stats: Male has priority over female. If neither male nor female, use Monster.

  • Monster units don't get random skills/equipment (i.e. random skills/requipment rolls are "Nothing"), but get fixed skills/equipment. (Not-player-controlled Monster-only Ovelia somehow loses her fixed equipment, too.)

  • Units with neither male nor female respect "Jobs Unlocked" during generation (unless monster), but start with 0 JP in all jobs. (The JP amounts might come from uninitialized memory, and should not be relied upon.)

  • Monster + Male and/or Female ignore Jobs Unlock. All jobs start at level 0, with 100-199 JP, unless neither male or female.

  • Job levels are recalculated upon joining. Monster + Male and/or Female end up with JLvs 1, and ungendered or Monster-only-gendered units end up with JLvs 0.

  • Starting with a given job only gives you levels in its direct requirements, not its reqs' reqs. (This doesn't matter in vanilla, because job reqs include all previous reqs. E.g. Monk requires Knight 2 AND Squire 2.)

  • The "Jobs Unlocked" setting can be higher than 8. Level 9 JP is 4280-4379, and Level 10 is about 32800, capped to 9999 after rolling learned skills. (These values could be from a buffer overread, so unrelated patches might change them.) Only the bottom four bits are read, so 16-31 is the same as 0-15.


Job tree:

  • Bard and Dancer gender requirement is exclusive, not inclusive. A male+female unit can access neither, while a genderless/monster-only unit can access both.

  • A Bard/Dancer requirement is ignored if the unit can't access the job due to gender. E.g. If Samurai (only) has a Bard 8 requirement, all females (including female+monster) can access it, because they can't be Bards. It MAY be possible to make jobs gender-specific by setting required Bard/Dancer level to 9, but FFTPatcher only allows requirement levels up to 8.

7
Bugs and Suggestions / Wiki restructuring
February 15, 2016, 02:25:46 am
Let's see how deep I can go with this.

== Categories vs namespaces ==

There are two features which both implement categorization: categories, and namespaces.

Namespaces:
- One each, or none (Main).
- Searchable: While searching, you can limit your searches to certain namespaces.
- Not searched (by default): A regular search will not search in namespaces other than (Main). A MediaWiki setting can add more namespaces to the default search.
- Requires typing the namespace to link to it.

Categories:
- Many.
- Not searchable (but see https://www.mediawiki.org/wiki/Extension:SphinxSearch)
- Searched, if in the searched namespaces.
- Does not change the page name, so does not need to be typed out.

== ASM code ==

(got distracted, forgot what this was about, except that it was the important part of this post)
8
Put into Greasemonkey.

// ==UserScript==
// @name        FFHwiki ASM helper
// @namespace   Raijinili
// @description FFH wiki ASM helper
// @include     http://ffhacktics.com/wiki/*
// @version     1.1
// @grant       none
// ==/UserScript==

var desc = {
'add': "Add add $d,$s,$t",
'addu': "Add unsigned addu $d,$s,$t",
'sub': "Subtract sub $d,$s,$t",
'subu': "Subtract unsigned subu $d,$s,$t",
'addi': "Add immediate addi $t,$s,C",
'addiu': "Add immediate unsigned addiu $t,$s,C",
'mult': "Multiply mult $s,$t",
'multu': "Multiply unsigned multu $s,$t",
'div': "Divide div $s, $t",
'divu': "Divide unsigned divu $s, $t",
'lw': "Load word lw $t,C($s)",
'lh': "Load halfword lh $t,C($s)",
'lhu': "Load halfword unsigned lhu $t,C($s)",
'lb': "Load byte lb $t,C($s)",
'lbu': "Load byte unsigned lbu $t,C($s)",
'sw': "Store word sw $t,C($s)",
'sh': "Store half sh $t,C($s)",
'sb': "Store byte sb $t,C($s)",
'lui': "Load upper immediate lui $t,C",
'mfhi': "Move from high mfhi $d",
'mflo': "Move from low mflo $d",
'mfcZ': "Move from Control Register mfcZ $t, $d",
'mtcZ': "Move to Control Register mtcZ $t, $d",
'and': "And and $d,$s,$t",
'andi': "And immediate andi $t,$s,C",
'or': "Or or $d,$s,$t",
'ori': "Or immediate ori $t,$s,C",
'xor': "Exclusive or xor $d,$s,$t",
'nor': "Nor nor $d,$s,$t",
'slt': "Set on less than slt $d,$s,$t",
'slti': "Set on less than immediate slti $t,$s,C",
'sll': "Shift left logical immediate sll $d,$t,shamt",
'srl': "Shift right logical immediate srl $d,$t,shamt",
'sra': "Shift right arithmetic immediate sra $d,$t,shamt",
'sllv': "Shift left logical sllv $d,$t,$s",
'srlv': "Shift right logical srlv $d,$t,$s",
'srav': "Shift right arithmetic srav $d,$t,$s",
'beq': "Branch on equal beq $s,$t,C",
'bne': "Branch on not equal bne $s,$t,C",
'j': "Jump j C",
'jr': "Jump register jr $s",
'jal': "Jump and link jal C",
'nop': "No operation (do nothing)",
};

var re = /^([0-9a-f]{8}: [0-9a-f]{8} )(\w+)(\s.*)?$/i;

function tooltipize(i, node){
var txt = node.textContent;
node.textContent = ''; //remove

//make new nodes and add them
txt.split(/\r?\n/)
.forEach(function parse(s){
var match = re.exec(s);
if(match == null){ //no asm detected
node.appendChild(document.createTextNode(s+'\n'));
return;
}

var prefix = document.createTextNode(match[1]);
var instr = match[2];
var suffix = document.createTextNode(match[3]+'\n');
var link = $("<a></a>", {
href: "./R3000_instruction set#"+instr,
title: desc[instr],
}).text(instr);
console.log(prefix, instr, suffix, link);
$(node).append(prefix, link, suffix);
});
}
$('div#mw-content-text pre').map(tooltipize);


It's a mockup of what can be done: tooltips and links for ASM instructions. Ideally, this would be done through wiki markup instead of Javascript, but this is fine until people figure out what they'd like out of it.