Documentação Comunitária

Guia Completo de Modding
The Sims 3

Scripts em C#, Jazz Scripts, animações personalizadas, object mods, pure scripting mods e criação de carreiras — tudo em um lugar.

C# / .NET Jazz Scripts Animações Object Mods Pure Script Mods Criação de Carreiras S3PE / S3OC PlaySoloAnimation ITUN Tuning
00

Ferramentas Necessárias

O que instalar antes de começar

Antes de qualquer coisa, você precisa das ferramentas corretas. A maioria dos tutoriais de modding para The Sims 3 gira em torno destas:

Visual Studio / SharpDevelop
IDE para escrever e compilar scripts em C#. SharpDevelop é mais leve; VS Community é mais completo.
S3PE
Sims 3 Package Editor. Indispensável para extrair DLLs do jogo e montar os arquivos .package.
S3OC
Sims 3 Object Cloner. Usado para clonar objetos existentes do jogo como base para novos mods.
.NET Reflector
Decompilador de assemblies. Permite explorar o código-fonte da EA para entender como o jogo funciona.
Smooth Jazz
Editor de Jazz Scripts. Essencial para criar e modificar sequências de animação.
AnimTool (Wes Howe)
Converte arquivos de animação entre formatos .animation e .smd.
Milkshape 3D / Blender
Software de modelagem 3D usado para criar e editar animações personalizadas.
STBL Creator (Twallan)
Cria tabelas de strings localizadas. Essencial para mods de carreira.

Extraindo as DLLs do Jogo

Antes de escrever qualquer código, você precisa extrair as bibliotecas do jogo para referenciar no seu projeto. Sem isso, o Visual Studio não saberá o que são classes como Sim, GameObject, Interaction, etc.

  1. Abra o S3PE e clique em File → Open…
  2. Navegue até a pasta de instalação do The Sims 3 e abra gameplay.package
  3. Clique em um recurso S3SA e use Resource → Export DLL
  4. Salve com o nome exato que o S3PE sugere (não renomeie)
  5. Repita para todos os recursos S3SA nos três packages: gameplay.package, scripts.package e simcore.package

Você deve terminar com estas DLLs na mesma pasta:

DLLPackage de Origem
Sims3GameplayObjects.dllgameplay.package
Sims3GameplaySystems.dllgameplay.package
Sims3StoreObjects.dllgameplay.package
UI.dllgameplay.package
SimIFace.dllscripts.package
ScriptCore.dllscripts.package
Sims3Metadata.dllscripts.package
System.Xml.dllsimcore.package
System.dllsimcore.package
mscorlib.dllsimcore.package
01

Object Mods

Criando objetos com scripts personalizados

Um Object Mod é sempre um objeto que usa um script personalizado. A forma mais básica é um clone de um objeto original com novas interações adicionadas. Este capítulo mostra como clonar um objeto, escrever um script para ele e fazer o objeto usar esse script.

Clonando um Objeto

  1. Abra o S3OC e vá em Clone → Normal Objects
  2. Role até o objeto que deseja clonar e clique em Clone or Fix
  3. Insira um nome de usuário único e mantenha as opções padrão
  4. Altere opções de catálogo como nome, descrição, preço ou categoria
  5. Clique em Start, escolha a pasta do seu projeto e dê um nome ao package

Encontrando a Classe Original e Iniciando o Script

Antes de escrever código, descubra qual classe o objeto original usa:

  1. Abra o package no S3PE
  2. Clique com o botão direito no recurso OBJK e escolha Edit OBJK
  3. No campo Script, você verá algo como Sims3.Gameplay.Objects.Miscellaneous.StuffedAnimal

Sua classe deve herdar dessa classe original. Por exemplo:

using System;
using Sims3.Gameplay.Objects.Miscellaneous;
using Sims3.Gameplay.Interactions;
using Sims3.Gameplay.Actors;
using Sims3.Gameplay.Autonomy;
using Sims3.SimIFace;
using Sims3.UI;

namespace Sims3.Gameplay.Objects.Miscellaneous.SeuUsernameAqui
{
    class TalkingTeddy : StuffedAnimal
    {
        // Sua classe herda todas as propriedades do StuffedAnimal
    }
}
⚠️ Namespace Único
Em object mods, o namespace deve começar com Sims3.Gameplay.Objects. Caso contrário, o jogo vai crashar quando você tentar comprar o objeto no catálogo. Nunca use o namespace de outro modder ou da EA.

Criando uma Interação

Interações são classes aninhadas dentro da sua classe de objeto. Aqui está a anatomia completa de uma interação simples:

public sealed class TalktoMe : ImmediateInteraction<Sim, TalkingTeddy>
{
    // Singleton: referência única da definição usada para adicionar a interação
    public static readonly InteractionDefinition Singleton = new Definition();

    // Run(): onde a ação acontece de fato
    protected override bool Run()
    {
        base.Actor.ShowTNSIfSelectable("Hello!", StyledNotification.NotificationStyle.kSimTalking);
        return true;
    }

    // Definition: define quando e como a interação aparece no pie menu
    [DoesntRequireTuning]
    private sealed class Definition : ImmediateInteractionDefinition<Sim, TalkingTeddy, TalkingTeddy.TalktoMe>
    {
        // GetInteractionName(): texto exibido no pie menu
        protected override string GetInteractionName(Sim a, TalkingTeddy target, InteractionObjectPair interaction)
        {
            return "Talk To Me";
        }

        // Test(): condição para a interação aparecer (return true = sempre aparece)
        protected override bool Test(Sim a, TalkingTeddy target, bool isAutonomous, ref GreyedOutTooltipCallback greyedOutTooltipCallback)
        {
            return !isAutonomous; // só aparece quando o jogador clica, não autonomamente
        }
    }
}
ℹ️ ImmediateInteraction vs Interaction
ImmediateInteraction: executa imediatamente, sem entrar na fila do Sim (ícone laranja no pie menu). Interaction: entra na fila normal do Sim e pode ser interrompida.

Adicionando a Interação ao Objeto

Dentro da sua classe de objeto (fora do código da interação), sobrescreva o método OnStartup():

protected override void OnStartup()
{
    base.OnStartup();
    base.AddInteraction(TalktoMe.Singleton);
}

Código Final Completo

using System;
using Sims3.Gameplay.Objects.Miscellaneous;
using Sims3.Gameplay.Interactions;
using Sims3.Gameplay.Actors;
using Sims3.Gameplay.Autonomy;
using Sims3.SimIFace;
using Sims3.UI;

namespace Sims3.Gameplay.Objects.Miscellaneous.KolipokiMod
{
    class TalkingTeddy : StuffedAnimal
    {
        public sealed class TalktoMe : ImmediateInteraction<Sim, TalkingTeddy>
        {
            public static readonly InteractionDefinition Singleton = new Definition();

            protected override bool Run()
            {
                base.Actor.ShowTNSIfSelectable("Hello!", StyledNotification.NotificationStyle.kSimTalking);
                return true;
            }

            [DoesntRequireTuning]
            private sealed class Definition : ImmediateInteractionDefinition<Sim, TalkingTeddy, TalkingTeddy.TalktoMe>
            {
                protected override string GetInteractionName(Sim a, TalkingTeddy target, InteractionObjectPair interaction)
                {
                    return "Talk To Me";
                }
                protected override bool Test(Sim a, TalkingTeddy target, bool isAutonomous, ref GreyedOutTooltipCallback greyedOutTooltipCallback)
                {
                    return !isAutonomous;
                }
            }
        }

        public override void OnStartup()
        {
            base.OnStartup();
            base.AddInteraction(TalktoMe.Singleton);
        }
    }
}

Adicionando o Script ao Package

  1. No S3PE, use Tools → FNV Hash para gerar um valor de instância único para o seu script. Digite algo como SeuUsername_NomeDoMod.dll e copie o FNV64.
  2. Clique em Resource → Add…, escolha tipo S3SA, coloque 0 no Group e o FNV64 no Instance.
  3. Com o recurso S3SA selecionado, use Resource → Import DLL e selecione o .dll compilado pelo VS (em bin\Release do projeto).
  4. Clique com o botão direito no recurso OBJKEdit OBJK e no campo Script coloque o nome completo da sua classe: Sims3.Gameplay.Objects.Miscellaneous.KolipokiMod.TalkingTeddy.
  5. Salve o package e teste no jogo.
02

Pure Script Mods

Mods sem vínculo com objetos específicos

Um Pure Script Mod não fica preso a um objeto específico. Ele usa código C# compilado em DLL, empacotado em S3SA, e um recurso XML que ativa uma variável marcada como [Tunable]. Quando o jogo resolve esse tuning, o construtor estático da classe é executado — abrindo a porta para registrar eventos, alarmes e injetar interações.

✅ O que esse tipo de mod faz bem
Rodar lógica assim que o mundo termina de carregar · Adicionar interações em Sims, objetos ou terreno · Reagir a eventos do jogo · Criar sistemas utilitários sem clonar objetos

Configuração do Projeto no Visual Studio

Depois de criar seu projeto compatível com o TS3, abra o arquivo AssemblyInfo.cs (em Properties) e adicione:

using Sims3.SimIFace;

[assembly: Tunable]
⚠️ Sem isso o mod não funciona
O atributo [assembly: Tunable] no AssemblyInfo.cs é obrigatório. Sem ele, o jogo não vai ativar corretamente o script via XML.

O Campo Tunable e Por Que Ele Existe

O .NET framework tem uma regra: na primeira vez que um campo, propriedade ou método estático de uma classe é acessado, o construtor estático daquela classe é chamado. O TS3 lê todos os XMLs e atribui os valores tunable às variáveis correspondentes nas classes. Logo — quando o jogo atribui um valor ao nosso campo estático, o construtor estático da classe é chamado automaticamente.

using System;
using Sims3.SimIFace;

namespace TwoBTech
{
    public class Pausinator
    {
        // Campo tunable: o jogo vai ler o XML e tentar atribuir um valor aqui.
        // Isso dispara o construtor estático!
        [Tunable]
        protected static bool kInstantiator = false;

        // Construtor estático: executado na primeira vez que a classe é acessada.
        static Pausinator()
        {
            // Aqui registramos nosso callback para quando o mundo carregar
            World.OnWorldLoadFinishedEventHandler += new EventHandler(OnWorldLoadFinished);
        }

        // Esse método roda sempre que uma vizinhança termina de carregar
        private static void OnWorldLoadFinished(object sender, EventArgs e)
        {
            // Seu código entra aqui!
        }
    }
}

Fazendo o Mod Fazer Algo

Exemplo clássico: pausar o jogo automaticamente ao carregar um save:

private static void OnWorldLoadFinished(object sender, EventArgs e)
{
    Sims3.Gameplay.Gameflow.SetGameSpeed(
        Gameflow.GameSpeed.Pause,
        Sims3.Gameplay.Gameflow.SetGameSpeedContext.GameStates
    );
}

Injetando Interações em Sims

Para adicionar uma opção no menu de Sims sem criar um object mod, combine três partes: a interação, a classe instanciadora e o XML.

Fluxo de execução
ParteFunção
Interaction classDefine o que aparece no menu e o que acontece quando o jogador escolhe a ação.
InstantiatorCarregado pelo XML, registra o callback de carregamento e injeta a interação.
Instantiator XMLAtiva o campo kInstantiator e garante que o construtor estático seja executado.

Injetando em todos os Sims ao carregar

private static void OnWorldLoadFinished(object sender, EventArgs e)
{
    foreach (Sim sim in Sims3.Gameplay.Queries.GetObjects<Sim>())
    {
        AddInteractions(sim);
    }
}

Evitando duplicatas

private static void AddInteractions(Sim sim)
{
    foreach (InteractionObjectPair pair in sim.Interactions)
    {
        // Se a interação já existe, não adiciona de novo
        if (pair.InteractionDefinition.GetType() == MinhaInteracao.Singleton.GetType())
        {
            return;
        }
    }
    sim.AddInteraction(MinhaInteracao.Singleton);
}
ℹ️ Sims criados após o carregamento
O carregamento inicial resolve apenas os Sims já existentes. Para recém-nascidos, imigrantes e outros Sims criados depois, use EventTracker para escutar eventos relevantes e aplicar a interação quando necessário.

Alarmes e Eventos

Dois mecanismos muito úteis para pure script mods:

Alarmes (AlarmManager)

using Sims3.Gameplay.Utilities;

private static void OnWorldLoadFinished(object sender, EventArgs e)
{
    // Dispara OnPauseAlarm após 1 segundo
    AlarmManager.Global.AddAlarm(
        1f,
        TimeUnit.Seconds,
        new AlarmTimerCallback(OnPauseAlarm),
        "Pause Alarm",
        AlarmType.NeverPersisted,
        null
    );
}

private static void OnPauseAlarm()
{
    Sims3.Gameplay.Gameflow.SetGameSpeed(Gameflow.GameSpeed.Pause,
        Sims3.Gameplay.Gameflow.SetGameSpeedContext.GameStates);
}

Eventos (EventTracker)

using Sims3.Gameplay.EventSystem;

// Em OnWorldLoadFinished, registrar o listener:
EventTracker.AddListener(
    EventTypeId.kSimEnteredVacationWorld,
    new ProcessEventDelegate(OnSimEnteredVacationWorld)
);

// O callback:
private static ListenerAction OnSimEnteredVacationWorld(Event e)
{
    Sims3.Gameplay.Gameflow.SetGameSpeed(Gameflow.GameSpeed.Pause,
        Sims3.Gameplay.Gameflow.SetGameSpeedContext.GameStates);
    return ListenerAction.Keep; // mantém o listener ativo
}

Escrevendo o XML e Montando o Package

O arquivo XML

O XML é mínimo. Ele só precisa definir um valor para o campo tunable. O nome do recurso XML deve corresponder exatamente a namespace.NomeDaClasse.

<?xml version="1.0" encoding="utf-8"?>
<base>
  <Current_Tuning>
    <kInstantiator value="True" />
  </Current_Tuning>
</base>

Montando no S3PE

  1. Crie um novo package (File → New)
  2. Adicione recurso S3SA → importe a DLL compilada
  3. Adicione recurso _XML (0x0333406C)
  4. No campo Instance/Name, coloque exatamente SeuNamespace.NomeDaClasse
  5. Importe o arquivo XML. Confirme no preview que começa com < e não com caracteres corrompidos
  6. Salve o package
🚨 Erro Comum
Se o nome do recurso XML não bater exatamente com a classe que contém o kInstantiator, o jogo não vai conectar o tuning ao código e o mod simplesmente não vai funcionar — sem erros visíveis.
03

Jazz Scripts

Controlando sequências de animação

Jazz Scripts controlam sequências de animação no The Sims 3. Tudo que anima no jogo envolve um Jazz Script — desde uma porta abrindo até uma interação social complexa.

ℹ️ A cadeia de 3 camadas
A animação é o efeito visual visível ao jogador. O Jazz Script controla como e quando os pedaços da animação são exibidos. O script C# inicia o Jazz Script (e pode encerrá-lo prematuramente se necessário).

Jazz Scripts NÃO são animações em si. Eles recebem instruções do código C# como "Faça o Sim X comer o alimento Y" e traduzem isso em animações específicas para o jogo executar.

State Machine

Um Jazz Script começa com uma declaração de State Machine. Tudo o mais fica dentro das chaves:

State Machine "Simoro_exercise"
{
    Priority High
    Property BlendMotionAccumulation
    Actor "x"
    Parameter "x:AthleticLevel" = 0
    Assign Actor "a_Simoro_exercise_all.ma"."x" as "x"

    // States ficam aqui dentro
}

Elementos da State Machine

ElementoDescrição
PriorityPrioridade padrão da state machine (veja tabela de prioridades)
PropertiesModificadores: Default, UnilateralActor, PinAllResources, BlendMotionAccumulation, HoldAllPoses
ActorDeclara todos os objetos envolvidos (sims, props, objetos). Cada um que tem animação precisa ser declarado.
ParameterVariáveis que podem ser definidas pelo código C#. Também definem o valor inicial.
Assign ActorAssocia clips de animação a atores. Ex: Assign Actor "a_exercise_all.ma"."x" as "x"

States e Transitions

Os States são onde as animações são reproduzidas. Cada state informa ao jogo quais animações vêm em seguida:

State "start_exercise"
{
    Properties Entry, Explicit
    Transitions to "exercise_loops"

    Play "a_Simoro_exercise_start_x" for "x"
    {
        Flags AtEnd, LoopAsNeeded
        Blend In 0.2
    }
}

Propriedades de State

PropriedadeDescrição
PublicState acessível externamente
EntryState de entrada, pode ser definido explicitamente sem usar transitions
ExitState de saída
LoopContinua voltando a esse state através de transitions
OneShotExecuta apenas uma vez
JoinState de junção
ExplicitPropriedade adicional (uso ainda não totalmente mapeado)

Por convenção, sempre existem três states padrão: State "Enter" (com Entry), State "Exit" (com Exit) e opcionalmente State "Join" (com Join).

Comando Play

O comando Play é o que de fato aciona a animação:

Play "a_Simoro_exercise_start_x" for "x"
{
    Flags AtEnd, LoopAsNeeded
    Blend In 0.2
}
✅ Dica Importante
Para animações de sims, mesmo que a animação seja projetada para criança ou adolescente, use sempre o prefixo a_ no comando Play. O jogo converte automaticamente. A exceção são animações personalizadas — nesse caso você precisa criar as versões para cada faixa etária.

Flags de Animação e Prioridades

FlagValorDescrição
AtEnd0x0001Padrão se nenhuma flag for especificada
LoopAsNeeded0x0002Animação se repete até a próxima começar
Mirror / OverrideMirror0x0008Espelha a animação
Interruptible0x0060Pode ser interrompida
HoldPose0x4000Congela na pose final da animação
BlendMotionAccumulation0x8000Combina acumulação de movimento

Tabela de Prioridades

NomeValor
Low6000
LowPlus8000
Normal10000
NormalPlus15000
FacialIdle17500
High20000
HighPlus25000
CarryRight30000
CarryLeft40000
Ultra50000
LookAt60000

Select Random e Select on Parameter

Seleção Aleatória

Select Random
{
    Flags AvoidRepeats  // Opcional: evita repetir antes de esgotar todas as opções
    Weight 1
    {
        Play "a_exercise_loop1_x" for "x"
    }
    Weight 1
    {
        Play "a_exercise_loop2_x" for "x"
    }
    Weight 10  // Esta opção tem 10x mais chance de ser escolhida
    {
        Play "a_exercise_loop3_x" for "x"
    }
}

Seleção por Parâmetro

Select on Parameter "x:Gender"
{
    Value "Male"
    {
        Play "a_Simoro_exercise_manly_workout_x" for "x"
    }
    Value "Female"
    {
        Play "a_Simoro_exercise_girly_workout_x" for "x"
    }
}

Next State

Next State "Finish_Workout"

Quando o jogo encontra esse comando, transita imediatamente para o state nomeado. O state chamado ainda precisa estar listado nas Transitions.

Props e Actor Operations

Criando Props

Create Prop "OldCrappyBaseball" as "baseball"
{
    Play "a2o_exercise_juggle_start_x" for "x"
    Play "a2o_exercise_juggle_start_baseBall" for "baseball"
}

Espelhando Animações (Actor Operation)

// Ativar espelhamento
Actor Operation SetMirror on "x"
{
    Actor Operation SetMirror on "baseball"
}

// Desativar
Actor Operation None on "x"
{
    Actor Operation None on "baseball"
}

Referência Rápida de Sintaxe

State Machine "nome_da_state_machine"
{
    Properties lista_de_propriedades   // Default, UnilateralActor, etc.
    Priority nivel_de_prioridade       // Low, Normal, High, Ultra, etc.
    Actor "nome_do_ator"               // "x", "y", "objeto", "prop", etc.
    Parameter "nome" = valor_padrao    // "x:Age" = "adult", etc.
    Assign Actor "grupo.ma"."ator" as "ator"

    State "nome_do_state"
    {
        Properties lista              // Public, Entry, Exit, Loop, etc.
        Transitions to "outro_state"

        // Escolha uma das opções abaixo:

        // Animação simples:
        Play "nome_da_animacao" for "x"
        {
            Flags AtEnd, LoopAsNeeded
            Blend In 0.2
        }

        // Seleção aleatória:
        Select Random
        {
            Flags AvoidRepeats
            Weight 1 { Play "anim1" for "x" }
            Weight 2 { Play "anim2" for "x" }
        }

        // Seleção por parâmetro:
        Select on Parameter "x:Age"
        {
            Value "adult" { Play "anim_adult" for "x" }
            Value "elder" { Play "anim_elder" for "x" }
        }

        // Ir para outro state:
        Next State "proximo_state"
    }
}
04

Animações Personalizadas

Criando e integrando CLIPs ao seu mod

Adicionar animações personalizadas requer coordenação entre três recursos: o arquivo CLIP (a animação em si), o Jazz Script (que define quando/como ela é reproduzida) e o script C# (que inicia a state machine).

Lado C#: Iniciando a State Machine

Dentro do método Run() da sua interação, use estas funções:

public override bool Run()
{
    // 1. Inicia a interação e configura LookAt
    base.StandardEntry();

    // 2. Diz ao jogo qual Jazz Script usar e qual é o state inicial
    //    Parâmetros: "NomeDaStateMachine", "StateInicial", "atorPrincipal"
    base.EnterStateMachine("TissueBoxAnim", "Enter", "x");

    // 3. Associa o objeto como ator
    //    Parâmetros: "NomeDoAtor", referência ao objeto
    base.SetActor("TissueBox_object", base.Target);

    // 4. Executa o state "Enter" no ator "x" (o sim)
    base.EnterState("x", "Enter");

    // 5. Vai para o state "Exit" (usando o sim como referência)
    base.AnimateSim("Exit");

    // 6. Encerra a interação (OBRIGATÓRIO!)
    base.StandardExit();

    return true;
}
🚨 Sempre use StandardExit()
Sem base.StandardExit() o jogo não sabe que a animação terminou. O Sim vai ficar "travado" executando a animação enquanto caminha, ou exibir comportamentos estranhos.

Referência de Funções de Animação

FunçãoDescrição
base.StandardEntry()Inicializa a interação e o LookAt
base.EnterStateMachine(nome, state, ator)Busca o Jazz Script e define o state inicial
base.SetActor(nome, objeto)Associa um objeto/sim como ator no Jazz Script
base.EnterState(ator, state)Executa um state do Jazz Script em um ator específico
base.AnimateSim(state)Versão simplificada de EnterState, sempre no ator "x"
base.RequestState(ator, state)Executa um state em qualquer ator (não apenas "x")
base.BeginCommodityUpdates()Ativa reconhecimento de traits/skills/buffs durante a animação
base.StandardExit()Encerra a animação e libera o Sim. Sempre necessário!

Jazz Script Básico para Objetos com Animação

State Machine "TissueBoxAnim"
{
    Actor "x"                          // O Sim
    Actor "TissueBox_object"           // O objeto

    Assign Actor "a_tissue_all.ma"."x" as "x"

    State "Enter"
    {
        Properties Public, Entry, Explicit
        Transitions to "DoAnim"
    }

    State "DoAnim"
    {
        Transitions to "Exit"

        Play "a_tissue_move_x" for "x"
        {
            Flags AtEnd
            Blend In 0.2
        }
    }

    State "Exit"
    {
        Properties Public, Exit, Explicit
    }
}

Preparando o Arquivo CLIP

  1. No S3PE, abra o FullBuild0.package (em %ProgramFiles%\Electronic Arts\The Sims 3\GameData\Shared\Packages)
  2. Localize uma animação base (recursos com tag CLIP) e exporte para a pasta do seu objeto
  3. Extraia o rigfile.txt do pacote de animações e coloque na mesma pasta
  4. Use o AnimTool: botão "Clip → SMD" para converter a animação para formato editável
  5. Abra o Milkshape 3D / Blender e importe o .smd para editar a animação
  6. Exporte de volta como .smd (sequência, não referência)
  7. Use o AnimTool: botão "SMD → Clip" para converter de volta para .animation
  8. No S3PE, adicione o .animation ao package do seu objeto

Convenções de Nomes de Animação

PrefixoTipo
a_Adulto (sim)
c_Criança
t_Adolescente
p_Toddler (criança pequena)
b_Bebê
e_Idoso
o_Objeto
a2a_Adulto para Adulto (interação social)
a2o_Adulto para Objeto
a2c_Adulto para Criança
ℹ️ Animações para Adolescentes
Os scripts da EA normalmente não incluem animações separadas para adolescentes, pois o jogo converte automaticamente as animações de adultos. Porém, animações personalizadas não fazem essa conversão automaticamente — você precisará criar versões com o prefixo t_ separadamente.

Jazz Script para Animação Personalizada (exemplo completo)

State Machine "animtutorialjazz"
{
    Actor "x"
    Assign Actor "a2o_test.ma"."x" as "x"

    State "Enter"
    {
        Properties Public, Entry, Explicit
        Transitions to "OurAnim"
    }

    State "OurAnim"
    {
        Transitions to "Exit"
        Play "a2o_test_x"
    }

    State "Exit"
    {
        Properties Public, Exit, Explicit
    }
}
05

Criação de Carreiras

Adicionando novas carreiras Rabbit Hole

Uma carreira Rabbit Hole é onde o Sim vai trabalhar e entra em um prédio, trabalhando por um período de tempo definido antes de voltar para casa. Diferente das carreiras autônomas (Ambições), você não pode ver o que o Sim está fazendo no trabalho.

Ferramentas para Criação de Carreiras

S3PE
Para abrir e editar arquivos .package
STBL Creator (Twallan)
Cria tabelas de strings localizadas para textos da carreira
Career Loader Mod (Twallan)
Necessário para que o jogo reconheça a nova carreira
Bootstrap Creator (Twallan)
Cria o arquivo de bootstrap necessário
⚠️ Atenção com maiúsculas/minúsculas
Se você nunca programou antes, saiba que letras maiúsculas e espaços fazem diferença no código. "MyCareer" é diferente de "myCareer" ou "My Career".

Planejamento da Carreira

Antes de começar a moddar, abra um arquivo de texto e defina todos os detalhes da carreira. Você precisará de:

Informações Básicas

ItemDescriçãoExemplo (Tênis)
Título da CarreiraNome exibido no jogo. NÃO adicione "carreira" no final.Tennis
Salários (10 níveis)Valor por hora em cada nível de promoção$17, $30, $40... até $750/h
Salário de aposentadoriaValor por dia após aposentar$40 a $1200/dia
Horário de acordarFormato 24h. Geralmente 2h antes de começar8, 8, 8, 7, 7, 6...
Horário de inícioFormato 24h por nível10, 10, 10, 9, 9, 8...
Duração do turnoHoras trabalhadas (aceita .5)7.5, 7, 7, 7, 7, 6...
Dias de trabalhoM,T,W,R,F,S,U (Seg a Dom)W,R,F,S,U nos primeiros níveis

Tipos de Carro Disponíveis

Defina qual carro busca o Sim em cada nível:

CarBusSchool  CarExpensive1  CarExpensive2  CarHatchback
CarLimoBlue  CarLimoPink   CarLimoWhite   CarNormal1
CarPickup2door  CarPolice  CarSedan       CarSports
CarUsed1  CarUsed2  CarVan4door  Limo  Taxi

Títulos de Cargo por Nível (exemplo Tênis)

NívelCargo
1Racquet Cleaner
2Ball Boy
3Lines Man
4Tennis Assistant
5Beginner Tennis
6Professional Player
7Challenger Tour Player
8250 and 500 Series Player
9Master 1000 Entry
10Grand Slam Champion

Tones (Comportamentos durante o Trabalho)

Tones são opções de comportamento que o Sim pode ter enquanto trabalha. Os tones padrão mais usados são:

  • Business as Usual — Trabalho e esforço normais
  • Work Hard — Trabalho intenso, mas reduz a diversão
  • Take It Easy — Reduz o estresse
  • Meet Co-Workers — Permite conhecer colegas de trabalho
  • Hang With Co-Workers — Melhora relacionamento com colegas
  • Suck Up to Boss — Melhora relacionamento com o chefe
  • Skill Booster — Aumenta uma habilidade relacionada à carreira
ℹ️ Sobre o tone Business as Usual
Aparentemente não é possível remover o tone "Business as Usual" — ele sempre será o primeiro tone de qualquer carreira. Os outros podem ser livremente personalizados.

Strings e Recursos de Texto

Os textos da carreira ficam em recursos STBL (string tables) dentro do package. Você precisará criar strings para:

  • Título da carreira
  • Descrição de entrada no emprego
  • Descrições de promoção (uma por nível)
  • Títulos dos cargos
  • Nomes e descrições dos tones
  • Nome e descrição dos co-workers
  • Texto de "Join Career"
06

PlaySoloAnimation

Animações simples em interações Sim→Sim e Sim→Objeto

PlaySoloAnimation() é o método mais direto para fazer um Sim executar uma animação dentro de uma interação — sem precisar de Jazz Script. Ele reproduz um único arquivo CLIP imediatamente, ideal para reações, gestos e animações rápidas.

ℹ️ PlaySoloAnimation vs Jazz Script
Use PlaySoloAnimation para animações únicas e simples (acenar, reagir, gesticular). Use Jazz Scripts quando precisar de sequências de animação, loops, props físicos, parâmetros dinâmicos ou sincronização entre dois atores.

Interação Sim → Sim com PlaySoloAnimation

O exemplo clássico é um Sim acenar para outro. A interação usa Interaction<Sim, Sim> — o ator e o alvo são ambos Sims:

using System;
using System.Collections.Generic;
using Sims3.Gameplay.Actors;
using Sims3.Gameplay.Autonomy;
using Sims3.Gameplay.EventSystem;
using Sims3.Gameplay.Interactions;
using Sims3.SimIFace;

namespace SeuUsername.WaveAtTutorial
{
    public class MushroomClass
    {
        [Tunable]
        protected static bool kActualize;

        static MushroomClass()
        {
            World.sOnWorldLoadFinishedEventHandler += new EventHandler(OnWorldLoadFinishedHandler);
        }

        public static void OnWorldLoadFinishedHandler(object sender, EventArgs e)
        {
            // Registra listener para Sims criados depois do carregamento
            EventTracker.AddListener(EventTypeId.kSimInstantiated,
                new ProcessEventDelegate(OnSimInstantiated));

            // Adiciona a interação a todos os Sims já existentes
            foreach (Sim sim in Sims3.Gameplay.Queries.GetObjects<Sim>())
            {
                if (sim != null)
                {
                    AddInteraction(sim);
                }
            }
        }

        // Chamado quando um novo Sim é criado/envelhece durante a jogatina
        public static ListenerAction OnSimInstantiated(Event e)
        {
            try
            {
                Sim sim = e.TargetObject as Sim;
                if (sim != null)
                {
                    AddInteraction(sim);
                }
            }
            catch (Exception) { }
            return ListenerAction.Keep;
        }

        public static void AddInteraction(Sim sim)
        {
            sim.AddInteraction(WaveAt.Singleton);
        }

        // ── A interação ──────────────────────────────────────────
        public sealed class WaveAt : Interaction<Sim, Sim>
        {
            public sealed class Definition : InteractionDefinition<Sim, Sim, WaveAt>
            {
                // Condição para aparecer no pie menu
                public override bool Test(Sim actor, Sim target, bool isAutonomous,
                    ref GreyedOutTooltipCallback greyedOutTooltipCallback)
                {
                    // Só crianças e acima, e não pode acenar para si mesmo
                    if (actor.SimDescription.ChildOrAbove)
                    {
                        return actor != target;
                    }
                    return false;
                }

                // Texto no pie menu
                public override string GetInteractionName(Sim actor, Sim target,
                    InteractionObjectPair iop)
                {
                    return "Wave at";
                }
            }

            // Singleton: referência única usada para registrar a interação
            public static readonly InteractionDefinition Singleton = new Definition();

            public override bool Run()
            {
                // Faz o ator virar para o alvo antes de acenar
                Actor.RouteTurnToFace(Target.Position);

                // Reproduz a animação de aceno
                // O jogo usa automaticamente a versão infantil para crianças
                Actor.PlaySoloAnimation("a_react_waveA_standing_x");

                return true;
            }
        }
    }
}

O que cada parte faz

LinhaFunção
Actor.RouteTurnToFace(Target.Position)Faz o ator girar para encarar o alvo antes da animação. Essencial para interações sociais.
Actor.PlaySoloAnimation("a_react_waveA_standing_x")Reproduz o CLIP especificado no ator. O jogo trata automaticamente variantes por faixa etária.
return trueSinaliza que a interação terminou com sucesso. false também encerra, mas indica falha.
✅ Conversão de faixa etária automática
Ao usar PlaySoloAnimation("a_react_waveA_standing_x"), o jogo aplica automaticamente a versão para crianças quando o ator for uma criança — mesmo que o nome comece com a_ (adulto). Você não precisa checar a idade manualmente para animações do jogo.

Interação Sim → Objeto com PlaySoloAnimation

Para interações onde o alvo é um objeto (não um Sim), use Interaction<Sim, SuaClasseDeObjeto>. O objeto precisa ter sido clonado ou ser uma classe existente do jogo.

public sealed class InspecionarObjeto : Interaction<Sim, MeuObjeto>
{
    public sealed class Definition : InteractionDefinition<Sim, MeuObjeto, InspecionarObjeto>
    {
        public override string GetInteractionName(Sim actor, MeuObjeto target,
            InteractionObjectPair iop)
        {
            return "Inspecionar";
        }

        public override bool Test(Sim actor, MeuObjeto target, bool isAutonomous,
            ref GreyedOutTooltipCallback greyedOutTooltipCallback)
        {
            return true; // sempre visível
        }
    }

    public static readonly InteractionDefinition Singleton = new Definition();

    public override bool Run()
    {
        // Faz o Sim caminhar até o objeto antes de agir
        if (!Actor.RouteToObject(Target))
        {
            return false; // não conseguiu chegar até o objeto
        }

        // Faz o Sim virar para o objeto
        Actor.RouteTurnToFace(Target.Position);

        // Animação de inspeção (olhar para baixo/objeto)
        Actor.PlaySoloAnimation("a_inspect_standing_x");

        return true;
    }
}

Roteamento até o objeto

MétodoDescrição
Actor.RouteToObject(Target)Faz o Sim caminhar até o objeto. Retorna false se não conseguir chegar.
Actor.RouteTurnToFace(Target.Position)Vira o Sim para encarar um ponto específico.
Actor.RouteToSlotAndCheckInUse(Target, Slots.Hash("Routing"))Rota até um slot específico do objeto e verifica se está em uso.

Variações de PlaySoloAnimation

O método PlaySoloAnimation tem variantes com parâmetros adicionais:

// Forma mais simples — só o nome do CLIP
Actor.PlaySoloAnimation("a_react_waveA_standing_x");

// Com bool para aguardar o fim da animação antes de continuar
// true  = espera a animação terminar
// false = continua o código enquanto a animação toca (em paralelo)
Actor.PlaySoloAnimation("a_react_waveA_standing_x", true);

// Especificando também o produto (para garantir compatibilidade de versão)
Actor.PlaySoloAnimation("a_react_waveA_standing_x", true, ProductVersion.BaseGame);
⚠️ Aguardar ou não aguardar?
Se você chamar PlaySoloAnimation("anim", false) e logo em seguida houver um return true, a interação pode terminar antes da animação acabar. Para animações que precisam terminar antes de continuar a lógica, use true como segundo parâmetro.

Arquivo ITUN (Interaction Tuning)

O arquivo ITUN é opcional, mas permite configurar comportamentos de IA — quanto de humor a interação dá, se Sims autônomos vão escolhê-la, para quais idades está disponível, etc.

ℹ️ Sem ITUN: use [DoesntRequireTuning]
Se você não criar um arquivo ITUN, adicione o atributo [DoesntRequireTuning] acima da classe Definition. Sem ele e sem o ITUN, o jogo vai ficar procurando um tuning que não existe.

Exemplo de arquivo ITUN

<?xml version="1.0"?>
<base>
  <!-- Nome completo: Namespace.Classe+InteractionClass -->
  <Interaction name="SeuUsername.WaveAtTutorial.MushroomClass+WaveAt" />
  <Object name="Sims3.Gameplay.Actors.Sim" />
  <CodeVersion name="BaseGame" considerCodeVersion="False" />
  <Current_Tuning>
    <Disallow
      DisallowAutonomous="False"
      DisallowUserDirected="False"
      DisallowPlayerSim="False" />
    <BaseAvailability>
      <!-- C=Criança, T=Teen, Y=YoungAdult, A=Adult, E=Elder -->
      <AgeSpeciesAvail AgeSpeciesValue="C,T,Y,A,E" />
      <MotiveThreshold MotiveThresholdType="None" MotiveThresholdValue="0" MotiveBelowCheck="False" />
      <MoodThreshold MoodThresholdType="None" MoodThresholdValue="0" />
    </BaseAvailability>
    <Tradeoff name="WaveAt">
      <Time value="1" addRoute="True" />
      <o>
        <!-- Fun: diz ao Sim que ganhará 20 de diversão; na prática ganha 10 -->
        <Change type="Fun"
          advertised="20" locked="True" actual="10"
          updateType="ImmediateDelta"
          timeDependsOn="False" updateEvenOnFailure="False"
          updateAboveAndBelowZero="Either" />

        <!-- Social: 10 de interação social contínua -->
        <Change type="Social"
          advertised="10" locked="True" actual="10"
          updateType="ContinuousFlow"
          timeDependsOn="False" updateEvenOnFailure="False"
          updateAboveAndBelowZero="Either" />

        <!-- Sims amigáveis têm 200x mais chance de fazer isso autonomamente -->
        <Change type="TraitFriendly"
          advertised="200" locked="True" actual="200"
          updateType="ImmediateDelta"
          timeDependsOn="False" updateEvenOnFailure="False"
          updateAboveAndBelowZero="Either" />
      </o>
    </Tradeoff>
  </Current_Tuning>
</base>

Campos do ITUN explicados

CampoDescrição
advertisedO quanto o Sim acha que vai ganhar (influencia decisões autônomas)
actualO quanto o Sim realmente ganha ao terminar
updateType="ImmediateDelta"Dá tudo de uma vez ao terminar a interação
updateType="ContinuousFlow"Dá ao longo da interação (bom para loops)
type="TraitFriendly"Bônus de probabilidade autônoma para Sims com o traço Amigável
AgeSpeciesAvailFaixas etárias que podem usar a interação: C, T, Y, A, E

Adicionando o ITUN ao Package no S3PE

  1. Vá em Resource → Add…, procure por ITUN e selecione
  2. Defina Group como 0 e dê um nome único (ex: SeuUsername_WaveAt_ITUN), clique em FNV64
  3. Com o recurso selecionado, abra-o no Notepad e cole o XML do ITUN
  4. Preencha o campo Interaction name com o caminho completo: Namespace.Classe+ClasseDeInteração — use + entre classes aninhadas, não ponto
  5. Salve e feche
✅ Nomenclatura do ITUN
A lógica do nome: use . para separar namespaces e a primeira classe. Use + para separar classes aninhadas (nested classes). Exemplo: Savanita.WaveAtTutorial.MushroomClass+WaveAt

Código Final Completo — Interação com PlaySoloAnimation

Exemplo funcional e completo de um pure script mod que adiciona uma interação "Acenar" de Sim para Sim, usando PlaySoloAnimation, com listener para novos Sims e suporte ao ITUN:

using System;
using System.Collections.Generic;
using Sims3.Gameplay.Actors;
using Sims3.Gameplay.Autonomy;
using Sims3.Gameplay.EventSystem;
using Sims3.Gameplay.Interactions;
using Sims3.SimIFace;

namespace SeuUsername.WaveAtTutorial
{
    public class MushroomClass
    {
        // ── Inicialização do mod ────────────────────────────────
        [Tunable]
        protected static bool kActualize;

        static MushroomClass()
        {
            World.sOnWorldLoadFinishedEventHandler += new EventHandler(OnWorldLoadFinishedHandler);
        }

        public static void OnWorldLoadFinishedHandler(object sender, EventArgs e)
        {
            // Listener para Sims criados/instanciados durante a jogatina
            EventTracker.AddListener(EventTypeId.kSimInstantiated,
                new ProcessEventDelegate(OnSimInstantiated));

            // Injeta a interação em todos os Sims já existentes
            foreach (Sim sim in Sims3.Gameplay.Queries.GetObjects<Sim>())
            {
                if (sim != null) AddInteraction(sim);
            }
        }

        public static ListenerAction OnSimInstantiated(Event e)
        {
            try
            {
                Sim sim = e.TargetObject as Sim;
                if (sim != null) AddInteraction(sim);
            }
            catch (Exception) { }
            return ListenerAction.Keep;
        }

        public static void AddInteraction(Sim sim)
        {
            sim.AddInteraction(WaveAt.Singleton);
        }

        // ── Interação WaveAt ────────────────────────────────────
        public sealed class WaveAt : Interaction<Sim, Sim>
        {
            public sealed class Definition : InteractionDefinition<Sim, Sim, WaveAt>
            {
                public override bool Test(Sim actor, Sim target, bool isAutonomous,
                    ref GreyedOutTooltipCallback greyedOutTooltipCallback)
                {
                    // Apenas crianças e acima, sem interagir consigo mesmo
                    return actor.SimDescription.ChildOrAbove && actor != target;
                }

                public override string GetInteractionName(Sim actor, Sim target,
                    InteractionObjectPair iop)
                {
                    return "Wave at";
                }
            }

            public static readonly InteractionDefinition Singleton = new Definition();

            public override bool Run()
            {
                // Vira o ator para o alvo
                Actor.RouteTurnToFace(Target.Position);

                // Reproduz a animação — o jogo converte para versão infantil se necessário
                Actor.PlaySoloAnimation("a_react_waveA_standing_x");

                return true;
            }
        }
    }
}

// ═══════════════════════════════════════════════════════════════
// EXEMPLO 2: Interação Sim → Objeto (inspecionar)
// ═══════════════════════════════════════════════════════════════

// Dentro da sua classe de objeto (no OnStartup):
// base.AddInteraction(InspecionarObjeto.Singleton);

public sealed class InspecionarObjeto : Interaction<Sim, MeuObjeto>
{
    public sealed class Definition : InteractionDefinition<Sim, MeuObjeto, InspecionarObjeto>
    {
        public override string GetInteractionName(Sim actor, MeuObjeto target,
            InteractionObjectPair iop)
        {
            return "Inspecionar";
        }

        [DoesntRequireTuning] // sem ITUN para esta interação
        public override bool Test(Sim actor, MeuObjeto target, bool isAutonomous,
            ref GreyedOutTooltipCallback greyedOutTooltipCallback)
        {
            return true;
        }
    }

    public static readonly InteractionDefinition Singleton = new Definition();

    public override bool Run()
    {
        // Caminha até o objeto — aborta se não conseguir chegar
        if (!Actor.RouteToObject(Target))
            return false;

        Actor.RouteTurnToFace(Target.Position);

        // true = aguarda a animação terminar antes de continuar
        Actor.PlaySoloAnimation("a_inspect_standing_x", true);

        return true;
    }
}
ℹ️ Encontrando nomes de animações do jogo
Para descobrir quais CLIPs existem no jogo, abra o FullBuild0.package no S3PE e filtre por recursos do tipo CLIP. Os nomes seguem as convenções de prefixo (a_, c_, a2a_ etc.) e são descritivos do que fazem.
07

Perguntas Frequentes

Casos práticos e soluções da comunidade

Adicionando Interações a Objetos Sem Clonar

kjmarket
Como adicionar uma interação a um objeto que está no jogo e não foi clonado, como FireworksBigBoom?
NonaMena

Primeiro, leia o tutorial de Pure Script Mod para criar sua classe instanciadora. Depois, use listeners e um método AddInteractions:

// Em OnWorldLoadFinished, adicione a interação aos objetos existentes:
List<FireworksBigBooms> list = new List<FireworksBigBooms>(
    Sims3.Gameplay.Queries.GetObjects<FireworksBigBooms>()
);
foreach (FireworksBigBooms current in list)
{
    Instantiator.AddInteractions(current);
}

// Registre um listener para quando novos objetos forem comprados:
EventTracker.AddListener(EventTypeId.kBoughtObject,
    new ProcessEventDelegate(Instantiator.OnObjectBought));

// Método para quando um objeto é comprado:
protected static ListenerAction OnObjectBought(Event e)
{
    if (e != null)
    {
        FireworksBigBooms obj = e.TargetObject as FireworksBigBooms;
        if (obj != null)
        {
            Instantiator.AddInteractions(obj);
        }
    }
    return ListenerAction.Keep;
}

// Método que adiciona a interação:
private static void AddInteractions(FireworksBigBooms obj)
{
    obj.AddInteraction(SuaInteracao.Singleton);
}

Listando Todos os Sims da Vizinhança

CmarNYC

Sims3.Gameplay.Queries.GetObjects<Sim>() retorna uma coleção de todos os sims que você pode iterar:

foreach (Sim sim in Sims3.Gameplay.Queries.GetObjects<Sim>())
{
    if (/* sua validação */)
    {
        // faça algo com o sim
    }
}

Criando um Picker Dialog (Lista de Seleção)

Em vez de um pie menu (que ficaria confuso com dezenas de sims), use um diálogo de lista:

// Dentro do Run() da sua interação:
List<ObjectListPickerInfo> pick = new List<ObjectListPickerInfo>();

foreach (Sim sim in Sims3.Gameplay.Queries.GetObjects<Sim>())
{
    pick.Add(new ObjectListPickerInfo(sim.Name, sim));
}

pick.Sort();
Object obj = ObjectListPickerDialog.Show(pick);

if (obj == null)
{
    return true; // usuário cancelou
}

Sim simSelecionado = (Sim)obj;
// Faça algo com o simSelecionado
⚠️ Erro comum em Run()
O método Run() é do tipo bool, então você deve retornar um valor boolean em todos os caminhos de código. Substitua return; por return true; e adicione um return true; ao final do método para evitar erros de compilação.

DLLs Desatualizadas — Objeto Crashando o Jogo

Se um objeto causa crash ao carregar, ou o Visual Studio não reconhece uma classe que deveria existir (como FireworksBase), é provável que suas DLLs extraídas estejam desatualizadas. Solução:

  1. Re-extraia todas as DLLs do jogo usando S3PE (os packages podem ter sido atualizados por patches)
  2. Atualize as referências do seu projeto Visual Studio para apontar para as novas DLLs
  3. Recompile o projeto

Boas Práticas e Erros Comuns

✅ Faça Isso
  • Use namespace único e pessoal
  • Centralize a injeção em uma classe instanciadora
  • Verifique duplicatas antes de adicionar interações
  • Teste o package com poucas mudanças por vez
  • Use o Reflector para estudar o código da EA
  • Sempre use StandardExit() em animações
  • Nomeie estados do Jazz Script de forma descritiva
🚨 Evite Isso
  • Esquecer [assembly: Tunable]
  • Errar o nome do recurso XML
  • Importar XML com encoding corrompido
  • Adicionar a mesma interação várias vezes
  • Assumir que o carregamento inicial cobre Sims criados depois
  • Modificar diretamente os scripts da EA (sempre duplique)
  • Usar o namespace de outro modder

Sobre Valores "Unknown Value" nos Jazz Scripts

Ao abrir scripts da EA no JazzData.package (em \The Sims 3\Game\Bin\Jazz\), você verá linhas como:

Unknown Value 0 = 0

Ninguém na comunidade sabe exatamente o que esses valores fazem. A maioria está zerada. Se você estiver modificando um script da EA, simplesmente os deixe como estão — não há razão para mexer neles.

✅ Modificando Scripts da EA
Nunca salve alterações no script original. Duplique-o, edite a cópia e coloque o script modificado no seu arquivo .package. O jogo vai automaticamente usar o script no .package em vez do original, desde que o nome da State Machine seja o mesmo.
07

Interações com PlaySoloAnimation

Animações rápidas sem Jazz Script — Sim→Sim e Sim→Objeto

PlaySoloAnimation é o jeito mais simples de rodar uma animação dentro de uma interação. Ao contrário do fluxo completo com EnterStateMachine + Jazz Script, aqui você passa diretamente o nome do clipe de animação — sem criar nenhum arquivo .jazz. É ideal para animações únicas e curtas, como acenar, reagir, comemorar, etc.

ℹ️ PlaySoloAnimation vs Jazz Script
Use PlaySoloAnimation quando a animação é simples, de uma só vez, e não depende de estados/loops. Use o fluxo Jazz Script quando precisar de múltiplos estados, animações condicionais, loops, props ou sincronizar dois atores.

Assinatura do método

// Versão básica — bloqueia até a animação terminar
Actor.PlaySoloAnimation(string clipName);

// Versão com controle de retorno:
// true = aguarda a animação terminar antes de continuar
// false = dispara e segue em paralelo
Actor.PlaySoloAnimation(string clipName, bool allowCarry, ProductVersion version);

// Versão mais comum nos mods:
Actor.PlaySoloAnimation("a_react_waveA_standing_x", true, ProductVersion.BaseGame);
✅ Prefixos de animação e idades
PlaySoloAnimation aceita prefixo a_ mesmo para Sims criança/adolescente — o jogo converte automaticamente para a versão correta da faixa etária quando o CLIP correspondente existe no jogo. Para animações personalizadas isso não acontece, você precisa criar os CLIPs por faixa etária manualmente.

Interação Sim → Sim com PlaySoloAnimation

O exemplo abaixo (tutorial de TheSweetSimmer) cria uma interação que faz um Sim acenar para outro Sim usando PlaySoloAnimation, tudo como Pure Script Mod (sem clonar objeto algum).

Estrutura geral

A interação Interaction<Sim, Sim> define que o ator (quem clica) e o alvo (quem recebe a ação) são ambos Sims. Dentro do Run():

  1. Girar o ator em direção ao alvo com RouteTurnToFace
  2. Reproduzir a animação com PlaySoloAnimation
  3. Retornar true para sinalizar sucesso
public sealed class WaveAt : Interaction<Sim, Sim>
{
    public sealed class Definition : InteractionDefinition<Sim, Sim, WaveAt>
    {
        public override bool Test(Sim actor, Sim target, bool isAutonomous,
            ref GreyedOutTooltipCallback greyedOutTooltipCallback)
        {
            // Só aparece para crianças e acima, e o Sim não pode acenar para si mesmo
            if (actor.SimDescription.ChildOrAbove)
            {
                return actor != target;
            }
            return false;
        }

        public override string GetInteractionName(Sim actor, Sim target, InteractionObjectPair iop)
        {
            return "Wave at";
        }
    }

    public static readonly InteractionDefinition Singleton = new Definition();

    public override bool Run()
    {
        // Faz o ator virar para o alvo antes de acenar
        Actor.RouteTurnToFace(Target.Position);

        // Reproduz o CLIP diretamente — sem Jazz Script
        Actor.PlaySoloAnimation("a_react_waveA_standing_x");

        return true;
    }
}

RouteTurnToFace e outros métodos de posicionamento

MétodoDescrição
Actor.RouteTurnToFace(Vector3 position)Gira suavemente o ator para encarar uma posição (ex: Target.Position)
Actor.RouteToObject(GameObject obj)Move o ator até o objeto antes de executar a interação
Actor.RouteToSlot(GameObject obj, ...)Move o ator para um slot específico do objeto (ex: slot de sentar)
Actor.RouteToPoint(Vector3 point)Move o ator para um ponto específico no mundo

Interação Sim → Objeto com PlaySoloAnimation

Quando o alvo é um objeto (e não outro Sim), muda o segundo parâmetro genérico da interação. O Sim continua sendo o ator, mas o target passa a ser uma classe que herda de GameObject.

Exemplo — Sim interage com um objeto e faz uma animação

using System;
using Sims3.Gameplay.Actors;
using Sims3.Gameplay.Autonomy;
using Sims3.Gameplay.Interactions;
using Sims3.Gameplay.Objects.Miscellaneous; // namespace do objeto alvo
using Sims3.SimIFace;
using Sims3.UI;

namespace Sims3.Gameplay.Objects.Miscellaneous.SeuUsername
{
    class MeuObjeto : StuffedAnimal // herda do objeto clonado
    {
        // ─── Interaction Sim → MeuObjeto ───
        public sealed class ExamineObject : Interaction<Sim, MeuObjeto>
        {
            public static readonly InteractionDefinition Singleton = new Definition();

            public override bool Run()
            {
                // Mover o Sim até o objeto antes de animar
                if (!Actor.RouteToObject(Target))
                {
                    return false; // não conseguiu chegar, cancela
                }

                // Girar para encarar o objeto
                Actor.RouteTurnToFace(Target.Position);

                // Animação de inspeção/surpresa (CLIP vanilla do jogo)
                Actor.PlaySoloAnimation("a_react_shocked_standing_x");

                // Notificação na tela com thumbnail do Sim
                Actor.ShowTNSIfSelectable(
                    "Que objeto interessante!",
                    StyledNotification.NotificationStyle.kSimTalking
                );

                return true;
            }

            [DoesntRequireTuning]
            private sealed class Definition : InteractionDefinition<Sim, MeuObjeto, ExamineObject>
            {
                protected override string GetInteractionName(Sim actor, MeuObjeto target, InteractionObjectPair iop)
                {
                    return "Examinar";
                }

                protected override bool Test(Sim actor, MeuObjeto target, bool isAutonomous,
                    ref GreyedOutTooltipCallback greyedOutTooltipCallback)
                {
                    return !isAutonomous; // só quando o jogador clica
                }
            }
        }

        // Registra a interação quando o objeto é carregado
        protected override void OnStartup()
        {
            base.OnStartup();
            base.AddInteraction(ExamineObject.Singleton);
        }
    }
}
ℹ️ Interação Sim → Objeto em Pure Script Mod
Se você quiser adicionar a interação a um objeto sem cloná-lo (Pure Script Mod), não use OnStartup(). Em vez disso, injete via OnWorldLoadFinished + listener kBoughtObject, exatamente como mostrado no Capítulo 02.

Mais CLIPs úteis para PlaySoloAnimation

Nome do CLIPDescrição
a_react_waveA_standing_xAcenar (acima da cabeça)
a_react_waveB_standing_xAcenar (lateral)
a_react_shocked_standing_xReação de choque/surpresa
a_react_cheer_standing_xComemoração/vibração
a_react_facepalm_standing_xMão no rosto (facepalm)
a_react_laugh_standing_xGargalhada
a_react_cry_standing_xChoro
a_react_clap_standing_xAplausos
a_react_danceSolo_standing_xDança solo
a_greet_handshake_standing_xAperto de mão
a_greet_hug_standing_xAbraço
✅ Como encontrar mais CLIPs
Abra o S3PEFullBuild0.package → filtre por tipo CLIP. Os nomes dos recursos seguem as convenções de prefixo explicadas no Capítulo 04 (a_, c_, t_, etc.). Use o Reflector para ver quais CLIPs os scripts da EA já utilizam como referência.

Injetando Interação Sim→Sim em Todos os Sims (Pure Script Completo)

Combinando tudo: namespace, campo [Tunable], construtor estático, injeção via OnWorldLoadFinished, listener kSimInstantiated para novos Sims, e PlaySoloAnimation no Run():

using System;
using Sims3.Gameplay.Actors;
using Sims3.Gameplay.Autonomy;
using Sims3.Gameplay.EventSystem;
using Sims3.Gameplay.Interactions;
using Sims3.SimIFace;
using System.Collections.Generic;

namespace Savanita.WaveAtTutorial
{
    public class MushroomClass
    {
        [Tunable]
        protected static bool kActualize;

        static MushroomClass()
        {
            World.sOnWorldLoadFinishedEventHandler += new EventHandler(OnWorldLoadFinishedHandler);
        }

        public static void OnWorldLoadFinishedHandler(object sender, EventArgs e)
        {
            // Escutar Sims que forem criados/envelhecerem durante a jogatina
            EventTracker.AddListener(
                EventTypeId.kSimInstantiated,
                new ProcessEventDelegate(OnSimInstantiated)
            );

            // Adicionar a interação a todos os Sims já no mundo
            foreach (Sim sim in Sims3.Gameplay.Queries.GetObjects<Sim>())
            {
                if (sim != null)
                {
                    AddInteraction(sim);
                }
            }
        }

        // Chamado quando um novo Sim é instanciado (nasceu, envelheceu, imigrou)
        public static ListenerAction OnSimInstantiated(Event e)
        {
            try
            {
                Sim sim = e.TargetObject as Sim;
                if (sim != null)
                {
                    AddInteraction(sim);
                }
            }
            catch (Exception) { }
            return ListenerAction.Keep;
        }

        public static void AddInteraction(Sim sim)
        {
            sim.AddInteraction(WaveAt.Singleton);
        }

        // ─── A Interação ───────────────────────────────────────
        public sealed class WaveAt : Interaction<Sim, Sim>
        {
            public sealed class Definition : InteractionDefinition<Sim, Sim, WaveAt>
            {
                public override bool Test(Sim actor, Sim target, bool isAutonomous,
                    ref GreyedOutTooltipCallback greyedOutTooltipCallback)
                {
                    if (actor.SimDescription.ChildOrAbove)
                    {
                        return actor != target;
                    }
                    return false;
                }

                public override string GetInteractionName(Sim actor, Sim target, InteractionObjectPair iop)
                {
                    return "Wave at";
                }
            }

            public static readonly InteractionDefinition Singleton = new Definition();

            public override bool Run()
            {
                // Vira o ator em direção ao Sim alvo
                Actor.RouteTurnToFace(Target.Position);

                // Reproduz o CLIP de animação diretamente
                Actor.PlaySoloAnimation("a_react_waveA_standing_x");

                return true;
            }
        }
    }
}

Arquivo ITUN — Tuning da Interação

O arquivo ITUN é opcional mas recomendado. Ele informa ao jogo como a interação afeta os humores, skills e comportamento autônomo dos Sims. Sem ele, adicione o atributo [DoesntRequireTuning] na sua Definition para o jogo não ficar procurando um ITUN que não existe.

Criando o ITUN no S3PE

  1. No S3PE: Resource → Add…, tipo ITUN
  2. Group = 0, nome único, clique em FNV64 para gerar o Instance
  3. Abra o recurso no Notepad e cole o XML abaixo
  4. No campo Interaction name="" coloque o nome completo: SeuNamespace.SuaClasse+SuaInteracao (use + para separar classes aninhadas, . para separar namespaces)

Exemplo de ITUN para a interação "Wave at"

<?xml version="1.0"?>
<base>
  <!-- Nome completo: namespace.ClassePai+ClasseInteracao -->
  <Interaction name="Savanita.WaveAtTutorial.MushroomClass+WaveAt" />
  <Object name="Sims3.Gameplay.Actors.Sim" />
  <CodeVersion name="BaseGame" considerCodeVersion="False" />
  <Current_Tuning>
    <Disallow
      DisallowAutonomous="False"
      DisallowUserDirected="False"
      DisallowPlayerSim="False" />
    <BaseAvailability>
      <!-- C=criança, T=teen, Y=jovem adulto, A=adulto, E=idoso -->
      <AgeSpeciesAvail AgeSpeciesValue="C,T,Y,A,E" />
      <MotiveThreshold MotiveThresholdType="None" MotiveThresholdValue="0" MotiveBelowCheck="False" />
      <MoodThreshold MoodThresholdType="None" MoodThresholdValue="0" />
      <SkillThreshold SkillThresholdType="None" SkillThresholdValue="0" />
      <CareerThreshold CareerThresholdType="Undefined" CareerThresholdValue="0" IncludePastCareers="False" />
      <Lot
        AllowNonGreetedSimsIfObjectOutside="False"
        AllowNonGreetedSimsIfObjectOutsideUserDirected="True"
        AllowGreetedSims="True"
        AllowOnCommunityLots="True"
        AllowOnAllLots="False" />
    </BaseAvailability>
    <Tradeoff name="Wave at">
      <Time value="1" addRoute="True" />
      <Exit funExit="False" stressExit="False" interruptible="False" />
      <o>
        <!-- Fun: advertised=quanto o jogo promete, actual=quanto o Sim realmente recebe -->
        <Change type="Fun"
          advertised="20" locked="True" actual="10"
          updateType="ImmediateDelta"
          timeDependsOn="False" updateEvenOnFailure="False"
          updateAboveAndBelowZero="Either" />

        <!-- Social: ContinuousFlow = aumenta gradualmente durante a interação -->
        <Change type="Social"
          advertised="10" locked="True" actual="10"
          updateType="ContinuousFlow"
          timeDependsOn="False" updateEvenOnFailure="False"
          updateAboveAndBelowZero="Either" />

        <!-- Sims com trait Friendly têm MUITO mais chance de usar isso autonomamente -->
        <Change type="TraitFriendly"
          advertised="200" locked="True" actual="200"
          updateType="ImmediateDelta"
          timeDependsOn="False" updateEvenOnFailure="False"
          updateAboveAndBelowZero="Either" />
      </o>
    </Tradeoff>
  </Current_Tuning>
</base>

Campos mais importantes do ITUN

CampoDescrição
advertisedQuanto de humor o Sim acha que vai receber (influi na decisão autônoma)
actualQuanto de humor o Sim realmente recebe
updateType="ImmediateDelta"Recebe tudo de uma vez ao terminar a interação
updateType="ContinuousFlow"Recebe gradualmente durante a interação (bom para loops)
AgeSpeciesAvailQuais faixas etárias podem usar: B=bebê, P=toddler, C=criança, T=teen, Y=jovem adulto, A=adulto, E=idoso
DisallowAutonomous="False"Sims podem usar a interação por conta própria (autonomamente)
TraitFriendlySims com o trait Friendly preferem muito mais esta interação
⚠️ Sem ITUN? Use [DoesntRequireTuning]
Se você não criar um ITUN, adicione [DoesntRequireTuning] acima da sua classe Definition para o jogo saber que não precisa procurar por um. Sem esse atributo e sem o ITUN, o jogo pode logar erros ou se comportar de forma inesperada.

Código Final Completo com Montagem do Package

Resumo dos passos para montar o package de um Pure Script Mod com PlaySoloAnimation:

RecursoTipo S3PEObservação
DLL compiladaS3SAGroup=0, Instance=FNV64 de um nome único seu
XML do Tunable_XML 0x0333406CInstance=FNV64 de Namespace.Classe exato
ITUN (opcional)ITUNInstance=FNV64 de qualquer nome único
  1. Compile o projeto no VS/SharpDevelop (Build → Build Solution). O .dll fica em bin\Release (VS) ou bin\Debug (SharpDevelop).
  2. No S3PE: File → New para criar um package vazio.
  3. Adicione o S3SA: Resource → Add, tipo S3SA, Group=0, gere Instance com FNV64 de um nome único. Com o recurso selecionado: Resource → Import DLL → selecione o .dll.
  4. Adicione o XML: Resource → Add, tipo _XML 0x0333406C, Group=0, Name=SeuNamespace.SuaClasse (EXATAMENTE igual ao código), FNV64. Abra no Notepad e cole o XML com <kActualize value="True" /> (ou o nome da sua variável tunable).
  5. Adicione o ITUN (opcional): Resource → Add, tipo ITUN, Group=0, nome único, FNV64. Cole o XML do ITUN e preencha o Interaction name.
  6. Salve o package com seu nome no arquivo (ex: SeuUsername_WaveAt.package) e coloque na pasta Mods\Packages do jogo.
✅ Dica de Nomenclatura do XML no S3PE
O nome do recurso XML (campo Name no S3PE, convertido para FNV64 como Instance) deve ser exatamente Namespace.NomeDaClasseComTunable. Se você tiver múltiplos namespaces separados por ponto, inclua todos. Exemplo: Savanita.WaveAtTutorial.MushroomClass.
A

Modelo Completo de Pure Script Mod

Copie, adapte e use como ponto de partida
using System;
using Sims3.Gameplay.Actors;
using Sims3.Gameplay.Autonomy;
using Sims3.Gameplay.EventSystem;
using Sims3.Gameplay.Interactions;
using Sims3.SimIFace;

namespace SeuNamespace.Common
{
    public static class Instantiator
    {
        // O jogo lê o XML e atribui um valor aqui, disparando o construtor estático
        [Tunable]
        internal static bool kInstantiator = false;

        static Instantiator()
        {
            World.OnWorldLoadFinishedEventHandler += new EventHandler(OnWorldLoadFinished);
        }

        private static void OnWorldLoadFinished(object sender, EventArgs e)
        {
            // Injetar interação em todos os Sims existentes
            foreach (Sim sim in Sims3.Gameplay.Queries.GetObjects<Sim>())
            {
                AddInteractions(sim);
            }

            // Para Sims criados depois, registre eventos via EventTracker
            // EventTracker.AddListener(EventTypeId.kSimInstantiated, ...);
        }

        private static void AddInteractions(Sim sim)
        {
            // Evita adicionar a mesma interação duas vezes
            foreach (InteractionObjectPair pair in sim.Interactions)
            {
                if (pair.InteractionDefinition.GetType() == MinhaInteracao.Singleton.GetType())
                {
                    return;
                }
            }
            sim.AddInteraction(MinhaInteracao.Singleton);
        }
    }

    // Exemplo de interação
    public sealed class MinhaInteracao : Interaction<Sim, Sim>
    {
        public static readonly InteractionDefinition Singleton = new Definition();

        public override bool Run()
        {
            // Sua lógica aqui
            return true;
        }

        [DoesntRequireTuning]
        private sealed class Definition : InteractionDefinition<Sim, Sim, MinhaInteracao>
        {
            public override string GetInteractionName(Sim actor, Sim target, InteractionObjectPair iop)
            {
                return "Minha Interação";
            }

            public override bool Test(Sim actor, Sim target, bool isAutonomous,
                ref GreyedOutTooltipCallback greyedOutTooltipCallback)
            {
                return actor != target; // Não pode interagir consigo mesmo
            }
        }
    }
}

Documentação compilada de: ModTheSims · SimsWiki · Simblr · tutoriais de Simoro, Lyralei, TheSweetSimmer, NonaMena, CmarNYC e outros membros da comunidade.
Este guia consolidado é para uso educativo e de referência.