mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 11:18:01 +00:00
Fixed enemy spawning in structures
reduced segmented WaveFunctionCollapse calculation for better performance added flying to enemies
This commit is contained in:
@@ -11,10 +11,13 @@ public class EnemyEdit extends FormPanel {
|
|||||||
EnemyData currentData;
|
EnemyData currentData;
|
||||||
JTextField nameField=new JTextField();
|
JTextField nameField=new JTextField();
|
||||||
JTextField colorField=new JTextField();
|
JTextField colorField=new JTextField();
|
||||||
JSpinner lifeFiled= new JSpinner(new SpinnerNumberModel(0, 0, 1000, 1));
|
JTextField ai=new JTextField();
|
||||||
JSpinner spawnRate= new JSpinner(new SpinnerNumberModel(0.0, 0., 1, 0.1));
|
JCheckBox flying=new JCheckBox();
|
||||||
JSpinner difficulty= new JSpinner(new SpinnerNumberModel(0.0, 0., 1, 0.1));
|
JCheckBox boss=new JCheckBox();
|
||||||
JSpinner speed= new JSpinner(new SpinnerNumberModel(0.0, 0., 100., 1.0));
|
FloatSpinner lifeFiled= new FloatSpinner(0, 1000, 1);
|
||||||
|
FloatSpinner spawnRate= new FloatSpinner( 0.f, 1, 0.1f);
|
||||||
|
FloatSpinner difficulty= new FloatSpinner( 0.f, 1, 0.1f);
|
||||||
|
FloatSpinner speed= new FloatSpinner( 0.f, 100.f, 1.0f);
|
||||||
FilePicker deck=new FilePicker(new String[]{"dck","json"});
|
FilePicker deck=new FilePicker(new String[]{"dck","json"});
|
||||||
FilePicker atlas=new FilePicker(new String[]{"atlas"});
|
FilePicker atlas=new FilePicker(new String[]{"atlas"});
|
||||||
JTextField equipment=new JTextField();
|
JTextField equipment=new JTextField();
|
||||||
@@ -36,13 +39,23 @@ public class EnemyEdit extends FormPanel {
|
|||||||
center.add("Sprite:",atlas);
|
center.add("Sprite:",atlas);
|
||||||
center.add("Equipment:",equipment);
|
center.add("Equipment:",equipment);
|
||||||
center.add("Colors:",colorField);
|
center.add("Colors:",colorField);
|
||||||
|
|
||||||
|
center.add("ai:",ai);
|
||||||
|
center.add("flying:",flying);
|
||||||
|
center.add("boss:",boss);
|
||||||
|
|
||||||
|
|
||||||
|
add(preview);
|
||||||
add(center);
|
add(center);
|
||||||
add(rewards);
|
add(rewards);
|
||||||
add(preview);
|
|
||||||
|
|
||||||
equipment.getDocument().addDocumentListener(new DocumentChangeListener(() -> EnemyEdit.this.updateEnemy()));
|
equipment.getDocument().addDocumentListener(new DocumentChangeListener(() -> EnemyEdit.this.updateEnemy()));
|
||||||
atlas.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(() -> EnemyEdit.this.updateEnemy()));
|
atlas.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(() -> EnemyEdit.this.updateEnemy()));
|
||||||
colorField.getDocument().addDocumentListener(new DocumentChangeListener(() -> EnemyEdit.this.updateEnemy()));
|
colorField.getDocument().addDocumentListener(new DocumentChangeListener(() -> EnemyEdit.this.updateEnemy()));
|
||||||
|
ai.getDocument().addDocumentListener(new DocumentChangeListener(() -> EnemyEdit.this.updateEnemy()));
|
||||||
|
flying.addChangeListener(e -> EnemyEdit.this.updateEnemy());
|
||||||
|
boss.addChangeListener(e -> EnemyEdit.this.updateEnemy());
|
||||||
|
|
||||||
nameField.getDocument().addDocumentListener(new DocumentChangeListener(() -> EnemyEdit.this.updateEnemy()));
|
nameField.getDocument().addDocumentListener(new DocumentChangeListener(() -> EnemyEdit.this.updateEnemy()));
|
||||||
deck.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(() -> EnemyEdit.this.updateEnemy()));
|
deck.getEdit().getDocument().addDocumentListener(new DocumentChangeListener(() -> EnemyEdit.this.updateEnemy()));
|
||||||
lifeFiled.addChangeListener(e -> EnemyEdit.this.updateEnemy());
|
lifeFiled.addChangeListener(e -> EnemyEdit.this.updateEnemy());
|
||||||
@@ -59,6 +72,9 @@ public class EnemyEdit extends FormPanel {
|
|||||||
return;
|
return;
|
||||||
currentData.name=nameField.getText();
|
currentData.name=nameField.getText();
|
||||||
currentData.colors=colorField.getText();
|
currentData.colors=colorField.getText();
|
||||||
|
currentData.ai=ai.getText();
|
||||||
|
currentData.flying=flying.isSelected();
|
||||||
|
currentData.boss=boss.isSelected();
|
||||||
currentData.life= (int) lifeFiled.getValue();
|
currentData.life= (int) lifeFiled.getValue();
|
||||||
currentData.sprite= atlas.getEdit().getText();
|
currentData.sprite= atlas.getEdit().getText();
|
||||||
if(equipment.getText().isEmpty())
|
if(equipment.getText().isEmpty())
|
||||||
@@ -88,6 +104,9 @@ public class EnemyEdit extends FormPanel {
|
|||||||
updating=true;
|
updating=true;
|
||||||
nameField.setText(currentData.name);
|
nameField.setText(currentData.name);
|
||||||
colorField.setText(currentData.colors);
|
colorField.setText(currentData.colors);
|
||||||
|
ai.setText(currentData.ai);
|
||||||
|
boss.setSelected(currentData.boss);
|
||||||
|
flying.setSelected(currentData.flying);
|
||||||
lifeFiled.setValue(currentData.life);
|
lifeFiled.setValue(currentData.life);
|
||||||
atlas.getEdit().setText(currentData.sprite);
|
atlas.getEdit().setText(currentData.sprite);
|
||||||
if(currentData.equipment!=null)
|
if(currentData.equipment!=null)
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public class SwingAtlasPreview extends Box {
|
|||||||
setSpritePath(sprite,null);
|
setSpritePath(sprite,null);
|
||||||
}
|
}
|
||||||
public void setSpritePath(String sprite,String name) {
|
public void setSpritePath(String sprite,String name) {
|
||||||
if(this.sprite==null||name==null||sprite==null||(this.sprite.equals(sprite)&&(spriteName==null&&spriteName.equals(name))))
|
if(this.sprite==null||sprite==null||(this.sprite.equals(sprite)&&(spriteName!=null&&name!=null&&spriteName.equals(name))))
|
||||||
return;
|
return;
|
||||||
removeAll();
|
removeAll();
|
||||||
counter=0;
|
counter=0;
|
||||||
|
|||||||
@@ -56,8 +56,14 @@ public class CharacterSprite extends MapActor {
|
|||||||
anim = atlas.createSprites(stand.toString());
|
anim = atlas.createSprites(stand.toString());
|
||||||
else
|
else
|
||||||
anim = atlas.createSprites(stand.toString() + dir.toString());
|
anim = atlas.createSprites(stand.toString() + dir.toString());
|
||||||
|
|
||||||
if (anim.size != 0) {
|
if (anim.size != 0) {
|
||||||
dirs.put(dir, new Animation<>(0.2f, anim));
|
dirs.put(dir, new Animation<>(0.2f, anim));
|
||||||
|
if(getWidth()==0.0)//init size onload
|
||||||
|
{
|
||||||
|
setWidth(anim.first().getWidth());
|
||||||
|
setHeight(anim.first().getHeight());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
animations.put(stand, dirs);
|
animations.put(stand, dirs);
|
||||||
|
|||||||
@@ -55,9 +55,8 @@ public class MapActor extends Actor {
|
|||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected void positionChanged() {
|
protected void positionChanged() {
|
||||||
|
|
||||||
updateBoundingRect();
|
|
||||||
super.positionChanged();
|
super.positionChanged();
|
||||||
|
updateBoundingRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ public class EnemyData {
|
|||||||
public boolean copyPlayerDeck = false;
|
public boolean copyPlayerDeck = false;
|
||||||
public String ai;
|
public String ai;
|
||||||
public boolean boss = false;
|
public boolean boss = false;
|
||||||
|
public boolean flying = false;
|
||||||
public float spawnRate;
|
public float spawnRate;
|
||||||
public float difficulty;
|
public float difficulty;
|
||||||
public float speed;
|
public float speed;
|
||||||
@@ -30,6 +31,7 @@ public class EnemyData {
|
|||||||
deck = enemyData.deck;
|
deck = enemyData.deck;
|
||||||
ai = enemyData.ai;
|
ai = enemyData.ai;
|
||||||
boss = enemyData.boss;
|
boss = enemyData.boss;
|
||||||
|
flying = enemyData.flying;
|
||||||
spawnRate = enemyData.spawnRate;
|
spawnRate = enemyData.spawnRate;
|
||||||
copyPlayerDeck = enemyData.copyPlayerDeck;
|
copyPlayerDeck = enemyData.copyPlayerDeck;
|
||||||
difficulty = enemyData.difficulty;
|
difficulty = enemyData.difficulty;
|
||||||
|
|||||||
@@ -225,7 +225,16 @@ public class NewGameScene extends UIScene {
|
|||||||
|
|
||||||
if(Forge.createNewAdventureMap)
|
if(Forge.createNewAdventureMap)
|
||||||
{
|
{
|
||||||
start();
|
FModel.getPreferences().setPref(ForgePreferences.FPref.UI_ENABLE_MUSIC, false);
|
||||||
|
WorldSave.generateNewWorld(selectedName.getText(),
|
||||||
|
gender.getCurrentIndex() == 0,
|
||||||
|
race.getCurrentIndex(),
|
||||||
|
avatarIndex,
|
||||||
|
deck.getCurrentIndex(),
|
||||||
|
Config.instance().getConfigData().difficulties[difficulty.getCurrentIndex()],
|
||||||
|
fantasyMode, easyMode, deck.getText(), 0);
|
||||||
|
GamePlayerUtil.getGuiPlayer().setName(selectedName.getText());
|
||||||
|
Forge.switchScene(SceneType.GameScene.instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ public class WorldStage extends GameStage implements SaveFileContent {
|
|||||||
enemyMoveVector.setLength(mob.speed()*delta);
|
enemyMoveVector.setLength(mob.speed()*delta);
|
||||||
tempBoundingRect.set(mob.getX()+ enemyMoveVector.x,mob.getY()+ enemyMoveVector.y,mob.getWidth(),mob.getHeight()*mob.getCollisionHeight());
|
tempBoundingRect.set(mob.getX()+ enemyMoveVector.x,mob.getY()+ enemyMoveVector.y,mob.getWidth(),mob.getHeight()*mob.getCollisionHeight());
|
||||||
|
|
||||||
if(WorldSave.getCurrentSave().getWorld().collidingTile(tempBoundingRect))//if direct path is not possible
|
if(!mob.getData().flying && WorldSave.getCurrentSave().getWorld().collidingTile(tempBoundingRect))//if direct path is not possible
|
||||||
{
|
{
|
||||||
tempBoundingRect.set(mob.getX()+ enemyMoveVector.x,mob.getY(),mob.getWidth(),mob.getHeight());
|
tempBoundingRect.set(mob.getX()+ enemyMoveVector.x,mob.getY(),mob.getWidth(),mob.getHeight());
|
||||||
if(WorldSave.getCurrentSave().getWorld().collidingTile(tempBoundingRect))//if only x path is not possible
|
if(WorldSave.getCurrentSave().getWorld().collidingTile(tempBoundingRect))//if only x path is not possible
|
||||||
@@ -238,7 +238,7 @@ public class WorldStage extends GameStage implements SaveFileContent {
|
|||||||
boolean enemyYIsBigger=sprite.getY()>player.getY();
|
boolean enemyYIsBigger=sprite.getY()>player.getY();
|
||||||
sprite.setX(player.getX() + spawnPos.x+(i*sprite.getWidth()*(enemyXIsBigger?1:-1)));//maybe find a better way to get spawn points
|
sprite.setX(player.getX() + spawnPos.x+(i*sprite.getWidth()*(enemyXIsBigger?1:-1)));//maybe find a better way to get spawn points
|
||||||
sprite.setY(player.getY() + spawnPos.y+(i*sprite.getHeight()*(enemyYIsBigger?1:-1)));
|
sprite.setY(player.getY() + spawnPos.y+(i*sprite.getHeight()*(enemyYIsBigger?1:-1)));
|
||||||
if(!WorldSave.getCurrentSave().getWorld().collidingTile(sprite.boundingRect()))
|
if(sprite.getData().flying || !WorldSave.getCurrentSave().getWorld().collidingTile(sprite.boundingRect()))
|
||||||
{
|
{
|
||||||
enemies.add(Pair.of(globalTimer,sprite));
|
enemies.add(Pair.of(globalTimer,sprite));
|
||||||
foregroundSprites.addActor(sprite);
|
foregroundSprites.addActor(sprite);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ public class BiomeStructure {
|
|||||||
boolean init=false;
|
boolean init=false;
|
||||||
private TextureAtlas structureAtlas;
|
private TextureAtlas structureAtlas;
|
||||||
public ColorMap image;
|
public ColorMap image;
|
||||||
|
private final static int MAXIMUM_WAVEFUNCTIONSIZE=50;
|
||||||
|
|
||||||
public BiomeStructure(BiomeStructureData data,long seed,int width,int height)
|
public BiomeStructure(BiomeStructureData data,long seed,int width,int height)
|
||||||
{
|
{
|
||||||
@@ -59,50 +60,62 @@ public class BiomeStructure {
|
|||||||
long currentTime = System.currentTimeMillis();
|
long currentTime = System.currentTimeMillis();
|
||||||
|
|
||||||
init=true;
|
init=true;
|
||||||
OverlappingModel model= new OverlappingModel(sourceImage,data.N, (int) (data.width* biomeWidth), (int) (data.height*biomeHeight),data.periodicInput,data.periodicOutput,data.symmetry,data.ground);
|
int targetWidth=(int) (data.width* biomeWidth);
|
||||||
|
int targetHeight=(int) (data.width* biomeWidth);
|
||||||
|
dataMap=new int[targetWidth][ targetHeight];
|
||||||
|
collisionMap=new boolean[targetWidth][ targetHeight];
|
||||||
|
ColorMap finalImage=new ColorMap(targetWidth, targetHeight);
|
||||||
HashMap<Integer,Integer> colorIdMap=new HashMap<>();
|
HashMap<Integer,Integer> colorIdMap=new HashMap<>();
|
||||||
for(int i=0;i<data.mappingInfo.length;i++)
|
for(int i=0;i<data.mappingInfo.length;i++)
|
||||||
{
|
{
|
||||||
colorIdMap.put(Integer.parseInt(data.mappingInfo[i].color,16),i);
|
colorIdMap.put(Integer.parseInt(data.mappingInfo[i].color,16),i);
|
||||||
}
|
}
|
||||||
|
for(int mx=0;mx<targetWidth;mx+=Math.min(targetWidth-mx,MAXIMUM_WAVEFUNCTIONSIZE))
|
||||||
|
{
|
||||||
|
for(int my=0;my<targetWidth;my+=Math.min(targetHeight-my,MAXIMUM_WAVEFUNCTIONSIZE))
|
||||||
|
{
|
||||||
|
OverlappingModel model= new OverlappingModel(sourceImage,data.N,Math.min(targetWidth-mx,MAXIMUM_WAVEFUNCTIONSIZE), Math.min(targetHeight-my,MAXIMUM_WAVEFUNCTIONSIZE),data.periodicInput,data.periodicOutput,data.symmetry,data.ground);
|
||||||
|
|
||||||
boolean suc=false;
|
boolean suc=false;
|
||||||
for(int i=0;i<10&&!suc;i++)
|
for(int i=0;i<10&&!suc;i++)
|
||||||
suc=model.run((int) seed+(i*5355),0);
|
suc=model.run((int) seed+(i*5355)+mx*my,0);
|
||||||
currentTime= System.currentTimeMillis();
|
|
||||||
if(!suc)
|
if(!suc)
|
||||||
{
|
{
|
||||||
dataMap=new int[(int) (data.width* biomeWidth)][ (int) (data.height*biomeHeight)];
|
|
||||||
collisionMap=new boolean[(int) (data.width* biomeWidth)][ (int) (data.height*biomeHeight)];
|
|
||||||
for(int x=0;x<dataMap.length;x++)
|
for(int x=0;x<dataMap.length;x++)
|
||||||
for(int y=0;y<dataMap[x].length;y++)
|
for(int y=0;y<dataMap[x].length;y++)
|
||||||
dataMap[x][y]=-1;
|
dataMap[mx+x][my+y]=-1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
image=model.graphics();
|
image=model.graphics();
|
||||||
dataMap=new int[image.getWidth()][image.getHeight()];
|
|
||||||
collisionMap=new boolean[image.getWidth()][image.getHeight()];
|
|
||||||
for(int x=0;x<image.getWidth();x++)
|
for(int x=0;x<image.getWidth();x++)
|
||||||
{
|
{
|
||||||
|
|
||||||
for(int y=0;y<image.getHeight();y++)
|
for(int y=0;y<image.getHeight();y++)
|
||||||
{
|
{
|
||||||
boolean isWhitePixel=maskImage!=null&&(maskImage.getColor((int) (x*maskImage.getWidth()/(float)image.getWidth()),(int)(y*(maskImage.getHeight()/(float)image.getHeight())) )).equals(Color.WHITE);
|
boolean isWhitePixel=maskImage!=null&&(maskImage.getColor((int) ((mx+x)*maskImage.getWidth()/(float)targetWidth),(int)((my+y)*(maskImage.getHeight()/(float)targetHeight)) )).equals(Color.WHITE);
|
||||||
|
|
||||||
if(isWhitePixel)
|
if(isWhitePixel)
|
||||||
image.setColor(x,y, Color.WHITE);
|
finalImage.setColor(mx+x,my+y, Color.WHITE);
|
||||||
|
else
|
||||||
|
finalImage.setColor(mx+x,my+y, image.getColor(x,y));
|
||||||
int rgb=Color.rgb888(image.getColor(x,y)) ;
|
int rgb=Color.rgb888(image.getColor(x,y)) ;
|
||||||
if(isWhitePixel||!colorIdMap.containsKey(rgb))
|
if(isWhitePixel||!colorIdMap.containsKey(rgb))
|
||||||
{
|
{
|
||||||
dataMap[x][y]=-1;
|
dataMap[mx+x][my+y]=-1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
dataMap[x][y]=colorIdMap.get(rgb);
|
dataMap[mx+x][my+y]=colorIdMap.get(rgb);
|
||||||
collisionMap[x][y]=data.mappingInfo[colorIdMap.get(rgb)].collision;
|
collisionMap[mx+x][my+y]=data.mappingInfo[colorIdMap.get(rgb)].collision;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
image=finalImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
initialize(sourceImage(),maskImage());
|
initialize(sourceImage(),maskImage());
|
||||||
|
|||||||
@@ -95,8 +95,8 @@
|
|||||||
"structureAtlasPath": "world/tilesets/structures.atlas",
|
"structureAtlasPath": "world/tilesets/structures.atlas",
|
||||||
"sourcePath": "world/tilesets/swamp_forest.png",
|
"sourcePath": "world/tilesets/swamp_forest.png",
|
||||||
"maskPath": "world/tilesets/ring.png",
|
"maskPath": "world/tilesets/ring.png",
|
||||||
"height": 0.4,
|
"height": 0.5,
|
||||||
"width": 0.4,
|
"width": 0.5,
|
||||||
"symmetry": 8,
|
"symmetry": 8,
|
||||||
"periodicOutput": false,
|
"periodicOutput": false,
|
||||||
"mappingInfo": [
|
"mappingInfo": [
|
||||||
|
|||||||
@@ -105,8 +105,8 @@
|
|||||||
"structureAtlasPath": "world/tilesets/structures.atlas",
|
"structureAtlasPath": "world/tilesets/structures.atlas",
|
||||||
"sourcePath": "world/tilesets/island_forest.png",
|
"sourcePath": "world/tilesets/island_forest.png",
|
||||||
"maskPath": "world/tilesets/ring.png",
|
"maskPath": "world/tilesets/ring.png",
|
||||||
"height": 0.4,
|
"height": 0.5,
|
||||||
"width": 0.4,
|
"width": 0.5,
|
||||||
"symmetry": 8,
|
"symmetry": 8,
|
||||||
"periodicOutput": false,
|
"periodicOutput": false,
|
||||||
"mappingInfo": [
|
"mappingInfo": [
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -115,8 +115,8 @@
|
|||||||
"structureAtlasPath": "world/tilesets/structures.atlas",
|
"structureAtlasPath": "world/tilesets/structures.atlas",
|
||||||
"sourcePath": "world/tilesets/lake.png",
|
"sourcePath": "world/tilesets/lake.png",
|
||||||
"maskPath": "world/tilesets/ring.png",
|
"maskPath": "world/tilesets/ring.png",
|
||||||
"height": 0.4,
|
"height": 0.5,
|
||||||
"width": 0.4,
|
"width": 0.5,
|
||||||
"periodicOutput": false,
|
"periodicOutput": false,
|
||||||
"mappingInfo": [
|
"mappingInfo": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -108,8 +108,8 @@
|
|||||||
"structureAtlasPath": "world/tilesets/structures.atlas",
|
"structureAtlasPath": "world/tilesets/structures.atlas",
|
||||||
"sourcePath": "world/tilesets/mountain.png",
|
"sourcePath": "world/tilesets/mountain.png",
|
||||||
"maskPath": "world/tilesets/ring.png",
|
"maskPath": "world/tilesets/ring.png",
|
||||||
"height": 0.4,
|
"height": 0.5,
|
||||||
"width": 0.4,
|
"width": 0.5,
|
||||||
"periodicOutput": false,
|
"periodicOutput": false,
|
||||||
"mappingInfo": [
|
"mappingInfo": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -120,8 +120,8 @@
|
|||||||
"structureAtlasPath": "world/tilesets/structures.atlas",
|
"structureAtlasPath": "world/tilesets/structures.atlas",
|
||||||
"sourcePath": "world/tilesets/hole.png",
|
"sourcePath": "world/tilesets/hole.png",
|
||||||
"maskPath": "world/tilesets/ring.png",
|
"maskPath": "world/tilesets/ring.png",
|
||||||
"height": 0.3,
|
"height": 0.5,
|
||||||
"width": 0.3,
|
"width": 0.5,
|
||||||
"symmetry": 8,
|
"symmetry": 8,
|
||||||
"periodicOutput": false,
|
"periodicOutput": false,
|
||||||
"mappingInfo": [
|
"mappingInfo": [
|
||||||
|
|||||||
@@ -99,8 +99,8 @@
|
|||||||
"structureAtlasPath": "world/tilesets/structures.atlas",
|
"structureAtlasPath": "world/tilesets/structures.atlas",
|
||||||
"sourcePath": "world/tilesets/plateau.png",
|
"sourcePath": "world/tilesets/plateau.png",
|
||||||
"maskPath": "world/tilesets/ring.png",
|
"maskPath": "world/tilesets/ring.png",
|
||||||
"height": 0.4,
|
"height": 0.5,
|
||||||
"width": 0.4,
|
"width": 0.5,
|
||||||
"periodicOutput": false,
|
"periodicOutput": false,
|
||||||
"mappingInfo": [
|
"mappingInfo": [
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user