Skip to main content

News

Topic: Project ZeC (My Zelda-esque Clone) (Read 61451 times) previous topic - next topic

0 Members and 3 Guests are viewing this topic.
  • Rhuan
  • [*][*][*][*]
Re: Project ZeC (My Zelda-esque Clone)
Reply #315
A bit tired to set up a full conversion for you right now - here's an RMP loader - with some comments pointing out the key stuff you'd need to write the bits you want back out again - if you can't work it out I could set it up for you another day, maybe saturday.

Basic idea to write it will be the following - note this is a starting point a working write function will be about 40 lines.
Code: [Select]
//make the output file
let outputFile = new DataStream(fileName.replace(".rmp",".js"), FileOp.Write);

//write out the strings
//you'd need to add the names and brakcets etc to them first
//the loaded versions will be function bodies only
outputFile.writeStringRaw(string1, string1.length);

Note this requires the latest beta of miniSphere 5 to run as it uses some new tricks.
Here's the RMP reading function - this takes an RMP fileName as an input loads the file and then returns an object containing all the data from the file - note you can't skip the read functions you don't need as the data is stored sequentially and each read moves you along in the file  - if you just need the map scripts it will be easier - the entity scripts are further down but the below does load them all.
Code: [Select]
import {DataStream} from "sphere-runtime";

function loadRMP(fileName)
{
let inputFile = new DataStream(fileName, FileOp.Read);

if(inputFile.readStringRaw(4) !== ".rmp")
{
throw new Error("rmpLoader provided with file that is not an rmp file filename is " + fileName);
}
inputFile.position = inputFile.position + 3;//skip version number (always 1) and type which is meaningless

let numLayers = inputFile.readUint8();

inputFile.position = inputFile.position + 1;//skip reserved byte

let numEntities = inputFile.readUint16(true);

//skip startX, startY, (Uint16s) startLayer, startDirection (Uint8s) <- not used with MEngine
inputFile.position = inputFile.position + 6;
let numStrings = inputFile.readUint16(true);

let numZones = inputFile.readUint16(true);

let repeating = inputFile.readUint8();
inputFile.position = inputFile.position + 234;

/*  -strings are all currently ignored, they are:
0 - tileset file (obsolete)
1 - music file
2 - script file (obsolete)
3 - entry script
4 - exit script
5 - north script
6 - east script
7 - south script
8 - west script*/
let mapStrings = new Array(9);
for(let i = 0; i < numStrings; ++i)
{
mapStrings[i] = inputFile.readString16(true);
}

let width = 0;
let height = 0;

//load main Layer data - 1st key output
let layers = new Array(numLayers);
for(let i = 0; i < numLayers; ++i)
{
layers[i] =
{
width       : inputFile.readUint16(true),
height      : inputFile.readUint16(true),
flags       : inputFile.readUint16(true),
parallaxX   : inputFile.readFloat32(true),
parallaxY   : inputFile.readFloat32(true),
scrollX     : inputFile.readFloat32(true),
scrollY     : inputFile.readFloat32(true),
numSegments : inputFile.readUint32(true),
reflective  : inputFile.readUint8(),
zones       : [],
triggers    : []
};
width = Math.max(width, layers[i].width);
height = Math.max(height, layers[i].height);
inputFile.position = inputFile.position + 3;//skip 3 reserved bytes
layers[i].name = inputFile.readString16(true);
layers[i].tiles = inputFile.read(2 * layers[i].width * layers[i].height);

layers[i].segments = new Array(layers[i].numSegments);
for(let j = 0; j < layers[i].numSegments; ++j)
{
let x = inputFile.readUint32(true);
let y = inputFile.readUint32(true);
layers[i].segments[j] = new Polygon(1, x, y, inputFile.readUint32(true) - x, inputFile.readUint32(true) - y);
}
}

//load entity data 2nd key output
//and load trigger data - attached into layer objects from above
let entities   = [];
for(let i = 0; i < numEntities; ++i)
{
let x     = inputFile.readUint16(true);
let y     = inputFile.readUint16(true);
let layer = inputFile.readUint16(true);
let type  = inputFile.readUint16(true);

inputFile.position = inputFile.position + 8;//skip 8 reserved bytes

if(layer > numLayers)
{
throw new Error("layer number " + layer + "for entity" + i + " but total layers in rmp are " + numLayers);
}
if(type == 1)
{
entities.push(
{
x          : x,
y          : y,
layer      : layer,
name       : inputFile.readString16(true),
sprite     : inputFile.readString16(true),//.replace(".rss",".ses"),
sripts     : new Array(5)
});
inputFile.position = inputFile.position + 2;//skip reading number of scripts as always 5
entities.scripts[0] = inputFile.readString16(true);
entities.scripts[1] = inputFile.readString16(true);
entities.scripts[2] = inputFile.readString16(true);
entities.scripts[3] = inputFile.readString16(true);
entities.scripts[4] = inputFile.readString16(true);

/* inputFile.position = inputFile.readUint16(true) + inputFile.position;//burn the scripts I don't want them
inputFile.position = inputFile.readUint16(true) + inputFile.position;
inputFile.position = inputFile.readUint16(true) + inputFile.position;
inputFile.position = inputFile.readUint16(true) + inputFile.position;
inputFile.position = inputFile.readUint16(true) + inputFile.position;*/
inputFile.position = inputFile.position + 16;//skip the reserved bytes
}
else if (type == 2)
{
layers[layer].triggers.push(
{
x      : x,
y      : y,
name : inputFile.readString16(true)//note per spec this is script - but no name and need an ID
});
}
}

//load zone data - attached into layer objects from above
for(let i = 0; i < numZones; ++i)
{
let x1 = inputFile.readUint16(true);
let y1 = inputFile.readUint16(true);
let x2 = inputFile.readUint16(true);
let y2 = inputFile.readUint16(true);
let layer = inputFile.readUint16(true);
let steps = inputFile.readUint16(true);
inputFile.position = inputFile.position + 4;
let name = inputFile.readString16(true);//script field re-purposed as a ref
layers[layer] = {
poly : new Polygon(1, x1, x2, x2 - x1, y2 - y1),
steps : steps,
name : name
};
}

if(inputFile.readStringRaw(4) !== ".rts")
{
throw new Error("Tile signiture not found either .rmp file is corrupt/out of date or there's an error in the reader script");
}
inputFile.position = inputFile.position + 2;//skip version number - always 1
let numTiles = inputFile.readUint16(true);
let tileWidth = inputFile.readUint16(true);
let tileHeight = inputFile.readUint16(true);
let tileSize = tileWidth * tileHeight;

inputFile.position = inputFile.position + 244;//skip 3 reserved bytes then 1 byte "has_obstructions" then 240 reserved bytes
//has_obstructions is aassumed to always be true

//load rawTile data 3rd key Output
let tileData = inputFile.read(numTiles * tileWidth * tileHeight * 4);

//load and process tile properties 4th key output
let tiles = new Array(numTiles);
for(let i = 0; i < numTiles; ++i)
{
inputFile.position = inputFile.position + 1;//skip 1 reserved byte
let animated = inputFile.readUint8();
let nextTile = inputFile.readUint16(true);
let delay = inputFile.readUint16(true);
inputFile.position = inputFile.position + 1;//skip 1 reserved byte
let obsType = inputFile.readUint8();
let numSegments = inputFile.readUint16(true);
let nameLength = inputFile.readUint16(true);
inputFile.position = inputFile.position + 20;//skip 20 reserved bytes

let tileName = inputFile.readStringRaw(nameLength);

let tileObs = [];
if(obsType == 1)
{
for(let j = 0; j < tileSize; ++j)
{
if(inputFile.readUint8() === 1)
{
tileObs.push(new Polygon(1, j % tileWidth, Math.floor(j / tileHeight), 1, 1));
}
}
}
else
{
for(let j = 0; j < numSegments; ++j)
{
let x1 = inputFile.readUint16(true);
let y1 = inputFile.readUint16(true);
let x2 = inputFile.readUint16(true);
let y2 = inputFile.readUint16(true);

let x_ = Math.min(x1, x2);
let y_ = Math.min(y1, y2);


tileObs.push(new Polygon(1, x_, y_,
Math.abs(x1 - x2),
Math.abs(y1 - y2)));
}
}
tiles[i] =
{
name     : tileName,
animated : animated,
nextTile : nextTile,
delay    : delay,
obs      : tileObs
};
}

return {
width      : width,
height     : height,
repeating  : repeating,
layers     : layers,
mapStrings : mapStrings,
entities   : entities,
numTiles   : numTiles,
tileWidth  : tileWidth,
tileHeight : tileHeight,
tileData   : tileData,
tiles      : tiles
};
}

class Polygon
{
constructor(type, x, y, w, h)
{
this.type = type;
this.x    = x;
this.y    = y;
this.w    = w;
this.h    = h;
}
}

Re: Project ZeC (My Zelda-esque Clone)
Reply #316
I've been creating a refined tileset for the Hyrulian Outpost map. I'll take a closer look at what you posted in a little bit.

The original and refined versions of the outpost:

Still making small adjustments to it.

Edit: Looking at the image it occurs to me that the 2 darker blue floor tiles were in fact the same color. Changed that.
  • Last Edit: September 28, 2017, 05:26:49 pm by Miscreant
"I am to misbehave." - Malcom Renyolds, Captain of Serenity

Re: Project ZeC (My Zelda-esque Clone)
Reply #317
I'm trying to figure out what is causing this one cave to crash the game.

I appear to have "fixed" the broken cave. I originally had it set with a trigger point. Kept crashing. I changed the trigger to a zone with the same line of code from the trigger point. Tested repeatedly in sphere and minisphere and now it's not crashing. Why would a trigger point crash it and a zone wouldn't... no idea.
  • Last Edit: September 28, 2017, 01:18:20 am by Miscreant
"I am to misbehave." - Malcom Renyolds, Captain of Serenity

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: Project ZeC (My Zelda-esque Clone)
Reply #318
Did it crash in miniSphere too, or only Sphere?
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

Re: Project ZeC (My Zelda-esque Clone)
Reply #319
Only in sphere.
"I am to misbehave." - Malcom Renyolds, Captain of Serenity

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: Project ZeC (My Zelda-esque Clone)
Reply #320
That makes sense, I remember trigger handling was pretty buggy (read: crashy) in Sphere 1.x.  As you've seen miniSphere is in general more stable and reliable than Sphere.  Building the engine was a combination of 1) reading the original source code and 2) reverse engineering, and wherever I found obvious bugs I fixed them.  So any time you have to ask "Why does this work right in miniSphere but not Sphere" you're probably just running into a bug that I purposely chose not to replicate ;)
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

Re: Project ZeC (My Zelda-esque Clone)
Reply #321
I've replaced quite a few of the trigger points with zones because I didn't really like the way they were processing. Such as some of my doors. The Island outpost door was particularly buggy as a trigger point.

Also, seeing as how I started this project with the 1.5 API (I originally found sphere then learned about the forums site and the wiki after I had already started coding), I intend to finish it with the 1.5. With my next project I'll delve into the realm of the v2 API.
  • Last Edit: September 28, 2017, 01:31:23 am by Miscreant
"I am to misbehave." - Malcom Renyolds, Captain of Serenity

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: Project ZeC (My Zelda-esque Clone)
Reply #322
Triggers in Sphere are kind of weird.  In most cases their area of effect doesn't line up with a tile boundary so that's probably why you're getting glitchy behavior with them.  In particular putting two triggers on adjacent tiles still allows the player just enough space to squeeze between them.  So I'd agree zones are almost surely the better choice here.

Hopefully by the time you dive into Sphere v2 I'll have mS 5.0 released and then you'll be able to take full advantage of it, ES6 syntax, modules and great JS performance (seriously even the current beta entirely blows mS 4.8 out of the water here) :D
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

Re: Project ZeC (My Zelda-esque Clone)
Reply #323
I have an rpg that I've been developing here and there on paper for a number of years. The previous languages I learned (VB, C++ (although I did get a C- in one of my C++ classes), Pascal) never really seemed to be the right enviroment to actually produce it.

While, ZeC is a fan made Zelda game that I hope, once complete, people will enjoy playing; it is my first introduction to sphere and javascript. Yet, it has already had, currently, 3 sub programs be created for it.

Just one of which is ZeC: World maps which I use often for dungeon level generation. That way I can get an idea of what the layout could be before I actually start coding it. I also used it to create the Overworld and Underworld maps.

As for the other RPG, I have a directory for it. I've started scripting the character, item and magic modules for it but I really haven't put that much into it yet because I would like to use miniSphere to create it. I'm also trying to design a main character that hasn't been seen countless times.
"I am to misbehave." - Malcom Renyolds, Captain of Serenity

Re: Project ZeC (My Zelda-esque Clone)
Reply #324
I've been thinking about how all the levels in the original LoZ fit together into one map...
"I am to misbehave." - Malcom Renyolds, Captain of Serenity

Re: Project ZeC (My Zelda-esque Clone)
Reply #325
In attempting to eliminate diagonal player movements (based on Original LoZ, only 4 movements n, s, e, w) the movement systems I've attempted so far have in fact eliminated the diagonal movement but in the process broken other essential game functions. Might be good movement systems for future projects but not ZeC.
"I am to misbehave." - Malcom Renyolds, Captain of Serenity

  • Rhuan
  • [*][*][*][*]
Re: Project ZeC (My Zelda-esque Clone)
Reply #326
In attempting to eliminate diagonal player movements (based on Original LoZ, only 4 movements n, s, e, w) the movement systems I've attempted so far have in fact eliminated the diagonal movement but in the process broken other essential game functions. Might be good movement systems for future projects but not ZeC.
To eliminate Diagonal movement whilst taking advantage of various "normal" features of the sphere v1 map engine you have to.

1. AttachInput to your character. (otherwise OnActivateTouch and related thigns don't work.

2. Use BindKey to overwrite the functionality that AttachInput has given to KEY_UP, KEY_DOWN, KEY_LEFT and KEY_RIGHT e.g.
Code: [Select]
BindKey(KEY_UP,"""");
BindKey(KEY_DOWN,"""");
BindKey(KEY_LEFT,"""");
BindKey(KEY_RIGHT,"""");
3. Implement new movement - normally done by setting an update script that handles it.

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: Project ZeC (My Zelda-esque Clone)
Reply #327
Does that BindKey trick work in miniSphere?  Thinking about how the input code in map_engine.c looks, it might not.
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Rhuan
  • [*][*][*][*]
Re: Project ZeC (My Zelda-esque Clone)
Reply #328
Does that BindKey trick work in miniSphere?  Thinking about how the input code in map_engine.c looks, it might not.
Honestly I have no idea, I've hardly used the "default" mapengine in years.

Re: Project ZeC (My Zelda-esque Clone)
Reply #329
I did all that, had a nice movement set. Broke functionality for the raft & ladder functions. Threw off all coordinates in dungeons (north & south doors were fine, east & west couldn't align the player to walk through them without redefining all x, y's). Never even attempted to try it in miniSphere due to the raft & ladder (2 very main functions) being broken. Set it back the way it was for the time being. I'll attempt other movement systems at some point. Eliminating diagonal movement is really just me being particular and doesn't really affect how the game is played.
"I am to misbehave." - Malcom Renyolds, Captain of Serenity