Ferramentas Necessárias
Antes de qualquer coisa, você precisa das ferramentas corretas. A maioria dos tutoriais de modding para The Sims 3 gira em torno destas:
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.
- Abra o S3PE e clique em File → Open…
- Navegue até a pasta de instalação do The Sims 3 e abra
gameplay.package - Clique em um recurso
S3SAe use Resource → Export DLL - Salve com o nome exato que o S3PE sugere (não renomeie)
- Repita para todos os recursos S3SA nos três packages:
gameplay.package,scripts.packageesimcore.package
Você deve terminar com estas DLLs na mesma pasta:
| DLL | Package de Origem |
|---|---|
Sims3GameplayObjects.dll | gameplay.package |
Sims3GameplaySystems.dll | gameplay.package |
Sims3StoreObjects.dll | gameplay.package |
UI.dll | gameplay.package |
SimIFace.dll | scripts.package |
ScriptCore.dll | scripts.package |
Sims3Metadata.dll | scripts.package |
System.Xml.dll | simcore.package |
System.dll | simcore.package |
mscorlib.dll | simcore.package |
Object Mods
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
- Abra o S3OC e vá em Clone → Normal Objects
- Role até o objeto que deseja clonar e clique em Clone or Fix
- Insira um nome de usuário único e mantenha as opções padrão
- Altere opções de catálogo como nome, descrição, preço ou categoria
- 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:
- Abra o package no S3PE
- Clique com o botão direito no recurso OBJK e escolha Edit OBJK
- 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
}
}
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
}
}
}
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
- No S3PE, use Tools → FNV Hash para gerar um valor de instância único para o seu script. Digite algo como
SeuUsername_NomeDoMod.dlle copie o FNV64. - Clique em Resource → Add…, escolha tipo S3SA, coloque 0 no Group e o FNV64 no Instance.
- Com o recurso S3SA selecionado, use Resource → Import DLL e selecione o .dll compilado pelo VS (em
bin\Releasedo projeto). - Clique com o botão direito no recurso OBJK → Edit OBJK e no campo Script coloque o nome completo da sua classe:
Sims3.Gameplay.Objects.Miscellaneous.KolipokiMod.TalkingTeddy. - Salve o package e teste no jogo.
Pure Script Mods
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.
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]
[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.
| Parte | Função |
|---|---|
Interaction class | Define o que aparece no menu e o que acontece quando o jogador escolhe a ação. |
Instantiator | Carregado pelo XML, registra o callback de carregamento e injeta a interação. |
Instantiator XML | Ativa 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);
}
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
- Crie um novo package (File → New)
- Adicione recurso S3SA → importe a DLL compilada
- Adicione recurso _XML (0x0333406C)
- No campo Instance/Name, coloque exatamente
SeuNamespace.NomeDaClasse - Importe o arquivo XML. Confirme no preview que começa com
<e não com caracteres corrompidos - Salve o package
kInstantiator, o jogo não vai conectar o tuning ao código e o mod simplesmente não vai funcionar — sem erros visíveis.
Jazz Scripts
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.
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
| Elemento | Descrição |
|---|---|
Priority | Prioridade padrão da state machine (veja tabela de prioridades) |
Properties | Modificadores: Default, UnilateralActor, PinAllResources, BlendMotionAccumulation, HoldAllPoses |
Actor | Declara todos os objetos envolvidos (sims, props, objetos). Cada um que tem animação precisa ser declarado. |
Parameter | Variáveis que podem ser definidas pelo código C#. Também definem o valor inicial. |
Assign Actor | Associa 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
| Propriedade | Descrição |
|---|---|
Public | State acessível externamente |
Entry | State de entrada, pode ser definido explicitamente sem usar transitions |
Exit | State de saída |
Loop | Continua voltando a esse state através de transitions |
OneShot | Executa apenas uma vez |
Join | State de junção |
Explicit | Propriedade 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
}
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
| Flag | Valor | Descrição |
|---|---|---|
AtEnd | 0x0001 | Padrão se nenhuma flag for especificada |
LoopAsNeeded | 0x0002 | Animação se repete até a próxima começar |
Mirror / OverrideMirror | 0x0008 | Espelha a animação |
Interruptible | 0x0060 | Pode ser interrompida |
HoldPose | 0x4000 | Congela na pose final da animação |
BlendMotionAccumulation | 0x8000 | Combina acumulação de movimento |
Tabela de Prioridades
| Nome | Valor |
|---|---|
Low | 6000 |
LowPlus | 8000 |
Normal | 10000 |
NormalPlus | 15000 |
FacialIdle | 17500 |
High | 20000 |
HighPlus | 25000 |
CarryRight | 30000 |
CarryLeft | 40000 |
Ultra | 50000 |
LookAt | 60000 |
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"
}
}
Animações Personalizadas
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;
}
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ção | Descriçã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
- No S3PE, abra o
FullBuild0.package(em%ProgramFiles%\Electronic Arts\The Sims 3\GameData\Shared\Packages) - Localize uma animação base (recursos com tag CLIP) e exporte para a pasta do seu objeto
- Extraia o
rigfile.txtdo pacote de animações e coloque na mesma pasta - Use o AnimTool: botão "Clip → SMD" para converter a animação para formato editável
- Abra o Milkshape 3D / Blender e importe o .smd para editar a animação
- Exporte de volta como .smd (sequência, não referência)
- Use o AnimTool: botão "SMD → Clip" para converter de volta para .animation
- No S3PE, adicione o .animation ao package do seu objeto
Convenções de Nomes de Animação
| Prefixo | Tipo |
|---|---|
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 |
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
}
}
Criação de Carreiras
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
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
| Item | Descrição | Exemplo (Tênis) |
|---|---|---|
| Título da Carreira | Nome 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 aposentadoria | Valor por dia após aposentar | $40 a $1200/dia |
| Horário de acordar | Formato 24h. Geralmente 2h antes de começar | 8, 8, 8, 7, 7, 6... |
| Horário de início | Formato 24h por nível | 10, 10, 10, 9, 9, 8... |
| Duração do turno | Horas trabalhadas (aceita .5) | 7.5, 7, 7, 7, 7, 6... |
| Dias de trabalho | M,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ível | Cargo |
|---|---|
| 1 | Racquet Cleaner |
| 2 | Ball Boy |
| 3 | Lines Man |
| 4 | Tennis Assistant |
| 5 | Beginner Tennis |
| 6 | Professional Player |
| 7 | Challenger Tour Player |
| 8 | 250 and 500 Series Player |
| 9 | Master 1000 Entry |
| 10 | Grand 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
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"
PlaySoloAnimation
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 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
| Linha | Funçã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 true | Sinaliza que a interação terminou com sucesso. false também encerra, mas indica falha. |
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étodo | Descriçã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);
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.
[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
| Campo | Descrição |
|---|---|
advertised | O quanto o Sim acha que vai ganhar (influencia decisões autônomas) |
actual | O 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 |
AgeSpeciesAvail | Faixas etárias que podem usar a interação: C, T, Y, A, E |
Adicionando o ITUN ao Package no S3PE
- Vá em Resource → Add…, procure por
ITUNe selecione - Defina Group como
0e dê um nome único (ex:SeuUsername_WaveAt_ITUN), clique em FNV64 - Com o recurso selecionado, abra-o no Notepad e cole o XML do ITUN
- Preencha o campo
Interaction namecom o caminho completo:Namespace.Classe+ClasseDeInteração— use+entre classes aninhadas, não ponto - Salve e feche
. 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;
}
}
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.
Perguntas Frequentes
Adicionando Interações a Objetos Sem Clonar
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
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
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:
- Re-extraia todas as DLLs do jogo usando S3PE (os packages podem ter sido atualizados por patches)
- Atualize as referências do seu projeto Visual Studio para apontar para as novas DLLs
- Recompile o projeto
Boas Práticas e Erros Comuns
- 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
- 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.
Interações com PlaySoloAnimation
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.
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);
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():
- Girar o ator em direção ao alvo com
RouteTurnToFace - Reproduzir a animação com
PlaySoloAnimation - Retornar
truepara 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étodo | Descriçã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);
}
}
}
OnStartup(). Em vez disso, injete via OnWorldLoadFinished + listener kBoughtObject, exatamente como mostrado no Capítulo 02.
Mais CLIPs úteis para PlaySoloAnimation
| Nome do CLIP | Descrição |
|---|---|
a_react_waveA_standing_x | Acenar (acima da cabeça) |
a_react_waveB_standing_x | Acenar (lateral) |
a_react_shocked_standing_x | Reação de choque/surpresa |
a_react_cheer_standing_x | Comemoração/vibração |
a_react_facepalm_standing_x | Mão no rosto (facepalm) |
a_react_laugh_standing_x | Gargalhada |
a_react_cry_standing_x | Choro |
a_react_clap_standing_x | Aplausos |
a_react_danceSolo_standing_x | Dança solo |
a_greet_handshake_standing_x | Aperto de mão |
a_greet_hug_standing_x | Abraço |
FullBuild0.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
- No S3PE: Resource → Add…, tipo ITUN
- Group = 0, nome único, clique em FNV64 para gerar o Instance
- Abra o recurso no Notepad e cole o XML abaixo
- 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
| Campo | Descrição |
|---|---|
advertised | Quanto de humor o Sim acha que vai receber (influi na decisão autônoma) |
actual | Quanto 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) |
AgeSpeciesAvail | Quais 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) |
TraitFriendly | Sims com o trait Friendly preferem muito mais esta interação |
[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:
| Recurso | Tipo S3PE | Observação |
|---|---|---|
| DLL compilada | S3SA | Group=0, Instance=FNV64 de um nome único seu |
| XML do Tunable | _XML 0x0333406C | Instance=FNV64 de Namespace.Classe exato |
| ITUN (opcional) | ITUN | Instance=FNV64 de qualquer nome único |
- Compile o projeto no VS/SharpDevelop (Build → Build Solution). O .dll fica em
bin\Release(VS) oubin\Debug(SharpDevelop). - No S3PE: File → New para criar um package vazio.
- 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.
- 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). - Adicione o ITUN (opcional): Resource → Add, tipo ITUN, Group=0, nome único, FNV64. Cole o XML do ITUN e preencha o
Interaction name. - Salve o package com seu nome no arquivo (ex:
SeuUsername_WaveAt.package) e coloque na pastaMods\Packagesdo jogo.
Namespace.NomeDaClasseComTunable. Se você tiver múltiplos namespaces separados por ponto, inclua todos. Exemplo: Savanita.WaveAtTutorial.MushroomClass.
Modelo Completo de Pure Script Mod
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.