nyan cat
@@ -6,7 +6,7 @@ minecraft_version=1.20.1
|
|||||||
yarn_mappings=1.20.1+build.10
|
yarn_mappings=1.20.1+build.10
|
||||||
loader_version=0.18.3
|
loader_version=0.18.3
|
||||||
# Mod Properties
|
# Mod Properties
|
||||||
mod_version=26.2.6
|
mod_version=26.2.7
|
||||||
maven_group=dev.tggamesyt
|
maven_group=dev.tggamesyt
|
||||||
archives_base_name=szar
|
archives_base_name=szar
|
||||||
# Dependencies
|
# Dependencies
|
||||||
|
|||||||
1
src/blockbench_models/NyanCat.bbmodel
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package dev.tggamesyt.szar.client;
|
||||||
|
|
||||||
|
import dev.tggamesyt.szar.NyanEntity;
|
||||||
|
import net.minecraft.client.model.*;
|
||||||
|
import net.minecraft.client.render.VertexConsumer;
|
||||||
|
import net.minecraft.client.render.entity.model.EntityModel;
|
||||||
|
import net.minecraft.client.util.math.MatrixStack;
|
||||||
|
|
||||||
|
// Made with Blockbench 5.0.3
|
||||||
|
// Exported for Minecraft version 1.17+ for Yarn
|
||||||
|
// Paste this class into your mod and generate all required imports
|
||||||
|
public class NyanCatEntityModel extends EntityModel<NyanEntity> {
|
||||||
|
private final ModelPart bb_main;
|
||||||
|
public NyanCatEntityModel(ModelPart root) {
|
||||||
|
this.bb_main = root.getChild("bb_main");
|
||||||
|
}
|
||||||
|
public static TexturedModelData getTexturedModelData() {
|
||||||
|
ModelData modelData = new ModelData();
|
||||||
|
ModelPartData modelPartData = modelData.getRoot();
|
||||||
|
ModelPartData bb_main = modelPartData.addChild("bb_main", ModelPartBuilder.create().uv(0, -35).cuboid(0.0F, -21.0F, -17.0F, 0.0F, 21.0F, 35.0F, new Dilation(0.0F)), ModelTransform.pivot(0.0F, 24.0F, 0.0F));
|
||||||
|
return TexturedModelData.of(modelData, 128, 128);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void setAngles(NyanEntity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) {
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void render(MatrixStack matrices, VertexConsumer vertexConsumer, int light, int overlay, float red, float green, float blue, float alpha) {
|
||||||
|
bb_main.render(matrices, vertexConsumer, light, overlay, red, green, blue, alpha);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package dev.tggamesyt.szar.client;
|
||||||
|
|
||||||
|
import dev.tggamesyt.szar.NyanEntity;
|
||||||
|
import net.minecraft.client.render.entity.EntityRendererFactory;
|
||||||
|
import net.minecraft.client.render.entity.MobEntityRenderer;
|
||||||
|
import net.minecraft.client.render.entity.model.EntityModel;
|
||||||
|
import net.minecraft.client.util.math.MatrixStack;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
|
||||||
|
public class NyanEntityRenderer extends MobEntityRenderer<NyanEntity, NyanCatEntityModel> {
|
||||||
|
|
||||||
|
private static final Identifier[] TEXTURES = new Identifier[12];
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (int i = 0; i < 12; i++) {
|
||||||
|
TEXTURES[i] = new Identifier("szar", "textures/entity/nyan_cat_textures/nyan_" + (i + 1) + ".png");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public NyanEntityRenderer(EntityRendererFactory.Context context) {
|
||||||
|
super(context, new NyanCatEntityModel(context.getPart(SzarClient.NYAN)), 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Identifier getTexture(NyanEntity entity) {
|
||||||
|
// Use age to cycle textures every 2 ticks
|
||||||
|
int index = (entity.age/ 1) % TEXTURES.length; // age is in ticks
|
||||||
|
return TEXTURES[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package dev.tggamesyt.szar.client;
|
package dev.tggamesyt.szar.client;
|
||||||
|
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
import dev.tggamesyt.szar.NyanEntity;
|
||||||
import dev.tggamesyt.szar.PlaneEntity;
|
import dev.tggamesyt.szar.PlaneEntity;
|
||||||
import dev.tggamesyt.szar.Szar;
|
import dev.tggamesyt.szar.Szar;
|
||||||
import dev.tggamesyt.szar.PlaneAnimation;
|
import dev.tggamesyt.szar.PlaneAnimation;
|
||||||
@@ -17,17 +18,22 @@ import net.minecraft.client.option.KeyBinding;
|
|||||||
import net.minecraft.client.render.entity.FlyingItemEntityRenderer;
|
import net.minecraft.client.render.entity.FlyingItemEntityRenderer;
|
||||||
import net.minecraft.client.render.entity.animation.Animation;
|
import net.minecraft.client.render.entity.animation.Animation;
|
||||||
import net.minecraft.client.render.entity.model.EntityModelLayer;
|
import net.minecraft.client.render.entity.model.EntityModelLayer;
|
||||||
|
import net.minecraft.client.sound.PositionedSoundInstance;
|
||||||
|
import net.minecraft.client.sound.SoundInstance;
|
||||||
|
import net.minecraft.client.sound.SoundManager;
|
||||||
import net.minecraft.client.util.InputUtil;
|
import net.minecraft.client.util.InputUtil;
|
||||||
import net.minecraft.client.render.*;
|
import net.minecraft.client.render.*;
|
||||||
|
import net.minecraft.client.world.ClientWorld;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.sound.SoundCategory;
|
||||||
|
import net.minecraft.sound.SoundEvent;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.util.math.Box;
|
||||||
import net.minecraft.util.math.MathHelper;
|
import net.minecraft.util.math.MathHelper;
|
||||||
import net.minecraft.util.math.random.Random;
|
import net.minecraft.util.math.random.Random;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.*;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import static dev.tggamesyt.szar.Szar.HitterEntityType;
|
import static dev.tggamesyt.szar.Szar.HitterEntityType;
|
||||||
import static dev.tggamesyt.szar.Szar.PLANE_ANIM_PACKET;
|
import static dev.tggamesyt.szar.Szar.PLANE_ANIM_PACKET;
|
||||||
@@ -37,12 +43,78 @@ public class SzarClient implements ClientModInitializer {
|
|||||||
private static final Map<KeyBinding, KeyBinding> activeScramble = new HashMap<>();
|
private static final Map<KeyBinding, KeyBinding> activeScramble = new HashMap<>();
|
||||||
public static final EntityModelLayer PLANE =
|
public static final EntityModelLayer PLANE =
|
||||||
new EntityModelLayer(
|
new EntityModelLayer(
|
||||||
new Identifier("szar", "plane"),
|
new Identifier(Szar.MOD_ID, "plane"),
|
||||||
"main"
|
"main"
|
||||||
);
|
);
|
||||||
|
public static final EntityModelLayer NYAN =
|
||||||
|
new EntityModelLayer(
|
||||||
|
new Identifier(Szar.MOD_ID, "nyan_cat"),
|
||||||
|
"main"
|
||||||
|
);
|
||||||
|
// Outside of your tick handler
|
||||||
|
private final Map<NyanEntity, PositionedSoundInstance> activeSounds = new HashMap<>();
|
||||||
|
private static final SoundEvent NYAN_LOOP = SoundEvent.of(new Identifier("szar", "nyan_cat_loop"));
|
||||||
|
private static final SoundEvent NYAN_START = SoundEvent.of(new Identifier("szar", "nyan_cat_first_loop"));
|
||||||
|
int startOffset = 10; // first tick when start sound plays
|
||||||
|
int startLength = 39; // length of one start sound
|
||||||
|
int loopLength = 542; // ticks per loop
|
||||||
@Override
|
@Override
|
||||||
public void onInitializeClient() {
|
public void onInitializeClient() {
|
||||||
|
ClientTickEvents.END_CLIENT_TICK.register(client -> {
|
||||||
|
if (client.world == null) return;
|
||||||
|
|
||||||
|
SoundManager soundManager = client.getSoundManager();
|
||||||
|
Box box = new Box(client.player.getX()-128, client.player.getY()-128, client.player.getZ()-128,
|
||||||
|
client.player.getX()+128, client.player.getY()+128, client.player.getZ()+128);
|
||||||
|
|
||||||
|
|
||||||
|
for (NyanEntity nyan : client.world.getEntitiesByClass(NyanEntity.class, box, e -> true)) {
|
||||||
|
// Skip dead ones (just in case)
|
||||||
|
if (!nyan.isAlive()) continue;
|
||||||
|
|
||||||
|
int age = nyan.age;
|
||||||
|
|
||||||
|
// Play first start
|
||||||
|
if (age == 10) {
|
||||||
|
PositionedSoundInstance start1 = PositionedSoundInstance.ambient(
|
||||||
|
NYAN_START, Random.create(),
|
||||||
|
nyan.getX(), nyan.getY(), nyan.getZ()
|
||||||
|
);
|
||||||
|
client.getSoundManager().play(start1);
|
||||||
|
activeSounds.put(nyan, start1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Play second start
|
||||||
|
if (age == 49) {
|
||||||
|
PositionedSoundInstance start2 = PositionedSoundInstance.ambient(
|
||||||
|
NYAN_START, Random.create(),
|
||||||
|
nyan.getX(), nyan.getY(), nyan.getZ()
|
||||||
|
);
|
||||||
|
client.getSoundManager().play(start2);
|
||||||
|
activeSounds.put(nyan, start2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Play looping
|
||||||
|
if (age >= 88 && (age - 88) % 542 == 0) {
|
||||||
|
PositionedSoundInstance loop = PositionedSoundInstance.ambient(
|
||||||
|
NYAN_LOOP, Random.create(),
|
||||||
|
nyan.getX(), nyan.getY(), nyan.getZ()
|
||||||
|
);
|
||||||
|
client.getSoundManager().play(loop);
|
||||||
|
activeSounds.put(nyan, loop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Iterator<Map.Entry<NyanEntity, PositionedSoundInstance>> it = activeSounds.entrySet().iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
Map.Entry<NyanEntity, PositionedSoundInstance> entry = it.next();
|
||||||
|
NyanEntity nyan = entry.getKey();
|
||||||
|
if (!nyan.isAlive()) {
|
||||||
|
client.getSoundManager().stop(entry.getValue()); // stop the sound immediately
|
||||||
|
it.remove(); // remove from map
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
ClientPlayNetworking.registerGlobalReceiver(
|
ClientPlayNetworking.registerGlobalReceiver(
|
||||||
PLANE_ANIM_PACKET,
|
PLANE_ANIM_PACKET,
|
||||||
(client, handler, buf, sender) -> {
|
(client, handler, buf, sender) -> {
|
||||||
@@ -112,7 +184,14 @@ public class SzarClient implements ClientModInitializer {
|
|||||||
PLANE,
|
PLANE,
|
||||||
PlaneEntityModel::getTexturedModelData
|
PlaneEntityModel::getTexturedModelData
|
||||||
);
|
);
|
||||||
|
EntityRendererRegistry.register(
|
||||||
|
Szar.NyanEntityType,
|
||||||
|
NyanEntityRenderer::new
|
||||||
|
);
|
||||||
|
EntityModelLayerRegistry.registerModelLayer(
|
||||||
|
NYAN,
|
||||||
|
NyanCatEntityModel::getTexturedModelData
|
||||||
|
);
|
||||||
EntityRendererRegistry.register(
|
EntityRendererRegistry.register(
|
||||||
Szar.GYPSY_ENTITY_TYPE,
|
Szar.GYPSY_ENTITY_TYPE,
|
||||||
GypsyEntityRenderer::new
|
GypsyEntityRenderer::new
|
||||||
|
|||||||
42
src/main/java/dev/tggamesyt/szar/NyanEntity.java
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package dev.tggamesyt.szar;
|
||||||
|
|
||||||
|
import net.minecraft.entity.EntityType;
|
||||||
|
import net.minecraft.entity.ai.goal.LookAroundGoal;
|
||||||
|
import net.minecraft.entity.ai.goal.MeleeAttackGoal;
|
||||||
|
import net.minecraft.entity.ai.goal.WanderAroundFarGoal;
|
||||||
|
import net.minecraft.entity.attribute.DefaultAttributeContainer;
|
||||||
|
import net.minecraft.entity.attribute.EntityAttributes;
|
||||||
|
import net.minecraft.entity.damage.DamageSource;
|
||||||
|
import net.minecraft.entity.mob.MobEntity;
|
||||||
|
import net.minecraft.entity.mob.PathAwareEntity;
|
||||||
|
import net.minecraft.sound.SoundCategory;
|
||||||
|
import net.minecraft.sound.SoundEvent;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
public class NyanEntity extends PathAwareEntity {
|
||||||
|
|
||||||
|
public NyanEntity(EntityType<? extends PathAwareEntity> type, World world) {
|
||||||
|
super(type, world);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initGoals() {
|
||||||
|
this.goalSelector.add(1, new WanderAroundFarGoal(this, 10.0D));
|
||||||
|
this.goalSelector.add(0, new LookAroundGoal(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static DefaultAttributeContainer.Builder createAttributes() {
|
||||||
|
return MobEntity.createMobAttributes()
|
||||||
|
.add(EntityAttributes.GENERIC_MAX_HEALTH, 20.0)
|
||||||
|
.add(EntityAttributes.GENERIC_MOVEMENT_SPEED, 0.25)
|
||||||
|
.add(EntityAttributes.GENERIC_ATTACK_DAMAGE, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void dropLoot(DamageSource source, boolean causedByPlayer) {
|
||||||
|
this.dropItem(Szar.POPTART);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,6 +37,7 @@ import net.minecraft.registry.tag.BiomeTags;
|
|||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
import net.minecraft.server.world.ServerWorld;
|
||||||
|
import net.minecraft.sound.SoundEvent;
|
||||||
import net.minecraft.sound.SoundEvents;
|
import net.minecraft.sound.SoundEvents;
|
||||||
import net.minecraft.structure.StructurePieceType;
|
import net.minecraft.structure.StructurePieceType;
|
||||||
import net.minecraft.text.Text;
|
import net.minecraft.text.Text;
|
||||||
@@ -110,6 +111,15 @@ public class Szar implements ModInitializer {
|
|||||||
SoundEvents.ENTITY_VILLAGER_WORK_CLERIC
|
SoundEvents.ENTITY_VILLAGER_WORK_CLERIC
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
public static final EntityType<NyanEntity> NyanEntityType =
|
||||||
|
Registry.register(
|
||||||
|
Registries.ENTITY_TYPE,
|
||||||
|
new Identifier(MOD_ID, "nyan_cat"),
|
||||||
|
FabricEntityTypeBuilder
|
||||||
|
.create(SpawnGroup.CREATURE, NyanEntity::new)
|
||||||
|
.dimensions(EntityDimensions.fixed(1.0F, 1.4F))
|
||||||
|
.build()
|
||||||
|
);
|
||||||
public static final EntityType<NiggerEntity> NiggerEntityType =
|
public static final EntityType<NiggerEntity> NiggerEntityType =
|
||||||
Registry.register(
|
Registry.register(
|
||||||
Registries.ENTITY_TYPE,
|
Registries.ENTITY_TYPE,
|
||||||
@@ -208,6 +218,8 @@ public class Szar implements ModInitializer {
|
|||||||
entries.add(Szar.CHEMICAL_WORKBENCH_ITEM);
|
entries.add(Szar.CHEMICAL_WORKBENCH_ITEM);
|
||||||
entries.add(Szar.AK_AMMO);
|
entries.add(Szar.AK_AMMO);
|
||||||
entries.add(Szar.AK47);
|
entries.add(Szar.AK47);
|
||||||
|
entries.add(Szar.POPTART);
|
||||||
|
entries.add(Szar.NYAN_SPAWNEGG);
|
||||||
})
|
})
|
||||||
.build()
|
.build()
|
||||||
);
|
);
|
||||||
@@ -358,6 +370,10 @@ public class Szar implements ModInitializer {
|
|||||||
NiggerEntityType,
|
NiggerEntityType,
|
||||||
NiggerEntity.createAttributes()
|
NiggerEntity.createAttributes()
|
||||||
);
|
);
|
||||||
|
FabricDefaultAttributeRegistry.register(
|
||||||
|
NyanEntityType,
|
||||||
|
NyanEntity.createAttributes()
|
||||||
|
);
|
||||||
FabricDefaultAttributeRegistry.register(
|
FabricDefaultAttributeRegistry.register(
|
||||||
NaziEntityType,
|
NaziEntityType,
|
||||||
NaziEntity.createAttributes()
|
NaziEntity.createAttributes()
|
||||||
@@ -668,6 +684,15 @@ public class Szar implements ModInitializer {
|
|||||||
new Identifier(MOD_ID, "fasz"),
|
new Identifier(MOD_ID, "fasz"),
|
||||||
new FaszItem(FASZ_BLOCK, new Item.Settings())
|
new FaszItem(FASZ_BLOCK, new Item.Settings())
|
||||||
);
|
);
|
||||||
|
public static final Item POPTART = Registry.register(
|
||||||
|
Registries.ITEM,
|
||||||
|
new Identifier(MOD_ID, "pop_tart"),
|
||||||
|
new Item(new Item.Settings()
|
||||||
|
.food(new FoodComponent.Builder()
|
||||||
|
.saturationModifier(0.6f).
|
||||||
|
hunger((Math.random() < 0.5) ? 6 : 7) // SIX OR SEVEN
|
||||||
|
.build()))
|
||||||
|
);
|
||||||
public static final Item NWORD_PASS = Registry.register(
|
public static final Item NWORD_PASS = Registry.register(
|
||||||
Registries.ITEM,
|
Registries.ITEM,
|
||||||
new Identifier(MOD_ID, "nwordpass"),
|
new Identifier(MOD_ID, "nwordpass"),
|
||||||
@@ -683,6 +708,16 @@ public class Szar implements ModInitializer {
|
|||||||
new Item.Settings()
|
new Item.Settings()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
public static final Item NYAN_SPAWNEGG = Registry.register(
|
||||||
|
Registries.ITEM,
|
||||||
|
new Identifier(MOD_ID, "nyan_cat_spawn_egg"),
|
||||||
|
new SpawnEggItem(
|
||||||
|
NyanEntityType,
|
||||||
|
0xFF99FF,
|
||||||
|
0xFF3399,
|
||||||
|
new Item.Settings()
|
||||||
|
)
|
||||||
|
);
|
||||||
public static final Item HITTER_SPAWNEGG = Registry.register(
|
public static final Item HITTER_SPAWNEGG = Registry.register(
|
||||||
Registries.ITEM,
|
Registries.ITEM,
|
||||||
new Identifier(MOD_ID, "hitler_spawn_egg"),
|
new Identifier(MOD_ID, "hitler_spawn_egg"),
|
||||||
|
|||||||
@@ -43,5 +43,9 @@
|
|||||||
"item.szar.ak47": "AK47",
|
"item.szar.ak47": "AK47",
|
||||||
"entity.szar.bullet": "Bullet",
|
"entity.szar.bullet": "Bullet",
|
||||||
"death.attack.bullet": "%1$s was shot by %2$s",
|
"death.attack.bullet": "%1$s was shot by %2$s",
|
||||||
"death.attack.bullet.player": "%1$s was shot by %2$s"
|
"death.attack.bullet.player": "%1$s was shot by %2$s",
|
||||||
|
|
||||||
|
"item.szar.pop_tart": "Pop Tart",
|
||||||
|
"entity.szar.nyan_cat": "Nyan Cat",
|
||||||
|
"item.szar.nyan_cat_spawn_egg": "Nyan Cat Spawn Egg"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:item/template_spawn_egg"
|
||||||
|
}
|
||||||
6
src/main/resources/assets/szar/models/item/pop_tart.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "szar:item/pop_tart"
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/main/resources/assets/szar/sounds.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"nyan_cat_first_loop": {
|
||||||
|
"sounds": [
|
||||||
|
{
|
||||||
|
"name": "szar:nyan_cat_first_loop",
|
||||||
|
"stream": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nyan_cat_loop": {
|
||||||
|
"sounds": [
|
||||||
|
{
|
||||||
|
"name": "szar:nyan_cat_loop",
|
||||||
|
"stream": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
src/main/resources/assets/szar/sounds/nyan_cat_first_loop.ogg
Normal file
BIN
src/main/resources/assets/szar/sounds/nyan_cat_loop.ogg
Normal file
83
src/main/resources/assets/szar/textures/entity/nyan.py
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
from PIL import Image
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def find_pixels_by_color(img, color):
|
||||||
|
"""Return list of (x, y) coordinates where the pixel matches the given color."""
|
||||||
|
width, height = img.size
|
||||||
|
pixels = img.load()
|
||||||
|
coords = []
|
||||||
|
for y in range(height):
|
||||||
|
for x in range(width):
|
||||||
|
if pixels[x, y][:3] == color: # ignore alpha if exists
|
||||||
|
coords.append((x, y))
|
||||||
|
return coords
|
||||||
|
|
||||||
|
|
||||||
|
def place_image(base_img, overlay_img, corner_coords):
|
||||||
|
"""Resize overlay_img to fit corner_coords and paste onto base_img (sharp, pixel-perfect)."""
|
||||||
|
if len(corner_coords) != 2:
|
||||||
|
raise ValueError("corner_coords must have exactly two points")
|
||||||
|
|
||||||
|
(x1, y1), (x2, y2) = corner_coords
|
||||||
|
# Calculate target box (left, top, right, bottom)
|
||||||
|
left = min(x1, x2)
|
||||||
|
top = min(y1, y2)
|
||||||
|
right = max(x1, x2)
|
||||||
|
bottom = max(y1, y2)
|
||||||
|
|
||||||
|
target_width = right - left + 1
|
||||||
|
target_height = bottom - top + 1
|
||||||
|
|
||||||
|
# Resize using NEAREST to keep pixels sharp
|
||||||
|
resized_overlay = overlay_img.resize((target_width, target_height), Image.Resampling.NEAREST)
|
||||||
|
base_img.paste(resized_overlay, (left, top), resized_overlay.convert("RGBA"))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def main(input_folder, texture_file, color1, color2, output_folder):
|
||||||
|
os.makedirs(output_folder, exist_ok=True)
|
||||||
|
|
||||||
|
texture = Image.open(texture_file).convert("RGBA")
|
||||||
|
texture_width, texture_height = texture.size
|
||||||
|
|
||||||
|
for filename in os.listdir(input_folder):
|
||||||
|
if not filename.lower().endswith(".png"):
|
||||||
|
continue
|
||||||
|
input_path = os.path.join(input_folder, filename)
|
||||||
|
overlay = Image.open(input_path).convert("RGBA")
|
||||||
|
|
||||||
|
# Create a transparent image of the same size as the texture
|
||||||
|
output_img = Image.new("RGBA", (texture_width, texture_height), (0, 0, 0, 0))
|
||||||
|
|
||||||
|
# Process first color
|
||||||
|
coords1 = find_pixels_by_color(texture, color1)
|
||||||
|
if len(coords1) != 2:
|
||||||
|
print(f"Warning: {filename} - color1 does not have exactly 2 pixels")
|
||||||
|
else:
|
||||||
|
place_image(output_img, overlay, coords1)
|
||||||
|
|
||||||
|
# Process second color (flipped horizontally)
|
||||||
|
coords2 = find_pixels_by_color(texture, color2)
|
||||||
|
if len(coords2) != 2:
|
||||||
|
print(f"Warning: {filename} - color2 does not have exactly 2 pixels")
|
||||||
|
else:
|
||||||
|
flipped_overlay = overlay.transpose(Image.FLIP_LEFT_RIGHT)
|
||||||
|
place_image(output_img, flipped_overlay, coords2)
|
||||||
|
|
||||||
|
# Save output
|
||||||
|
output_path = os.path.join(output_folder, filename)
|
||||||
|
output_img.save(output_path)
|
||||||
|
print(f"Saved {output_path}")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Example usage
|
||||||
|
input_folder = "nyan_cat_input"
|
||||||
|
texture_file = "nyan_cat_example.png"
|
||||||
|
color2 = (255, 0, 0) # red
|
||||||
|
color1 = (0, 138, 255) # blue
|
||||||
|
output_folder = "nyan_cat_textures"
|
||||||
|
|
||||||
|
main(input_folder, texture_file, color1, color2, output_folder)
|
||||||
BIN
src/main/resources/assets/szar/textures/entity/nyan_cat.gif
Normal file
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 886 B |
|
After Width: | Height: | Size: 461 B |
|
After Width: | Height: | Size: 455 B |
|
After Width: | Height: | Size: 457 B |
|
After Width: | Height: | Size: 459 B |
|
After Width: | Height: | Size: 466 B |
|
After Width: | Height: | Size: 453 B |
|
After Width: | Height: | Size: 455 B |
|
After Width: | Height: | Size: 457 B |
|
After Width: | Height: | Size: 459 B |
|
After Width: | Height: | Size: 461 B |
|
After Width: | Height: | Size: 456 B |
|
After Width: | Height: | Size: 453 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
BIN
src/main/resources/assets/szar/textures/entity/smooth.zip
Normal file
BIN
src/main/resources/assets/szar/textures/item/pop_tart.png
Normal file
|
After Width: | Height: | Size: 811 B |