Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • PantyNabber/fc-pregmod
  • pregmodfan/fc-pregmod
  • Alice.Grant/fc-pregmod
  • powerfful/fc-pregmod
  • elstumpo/fc-pregmod
  • Y/fc-pregmod
  • floer/fc-pregmod
  • oidocrop/fc-pregmod
  • hawk5005/fc-pregmod
  • nn/fc-pregmod
  • latios298/fc-pregmod
  • brpregmodfan/fc-pregmod
  • SomeoneTookMyUserName/fc-pregmod
  • 000-250-006/fc-pregmod
  • rewrica/fc-pregmod
  • Stuffedgame/fc-pregmod
  • wisepants314/fc-pregmod
  • fcanon/fc-pregmod
  • randomanon1/pregmod-mod-cyberfurry
  • teddy.buckland/fc-pregmod
  • farsinian_591b7a2d8b49d/fc-pregmod
  • FCShotadev/fc-pregmod
  • uselessartifact/fc-pregmod
  • irina_90/fc-pregmod
  • thaumx/fc-pregmod
  • MouseOfLight/fc-pregmod
  • empresssela/fc-pregmod
  • MasterAaran/fc-pregmod
  • ishy/fc-pregmod
  • psychofox/fc-pregmod
  • shadomancer/fc-pregmod
  • tycrakgg/fc-pregmod
  • azktaawc/fc-pregmod
  • andonno07/fc-pregmod
  • Onithyr/fc-pregmod
  • redneck987.jl/fc-pregmod
  • Farseeker/fc-pregmod
  • milliarc/fc-pregmod
  • BeefimusMaximus/fc-pregmod
  • magicknight79/fc-pregmod
  • hexall90/fc-pregmod
  • cantworkforever/fc-pregmod
  • jc052005/fc-pregmod
  • klorpa/fc-pregmod
  • doku/fc-pregmod
  • samhradh/fc-pregmod
  • scientist/fc-pregmod
  • albania420/fc-pregmod
  • Editoranon/fc-pregmod
  • Anony/fc-pregmod
  • deimios666/fc-pregmod
  • IvoHoe/fc-pregmod
  • bitty/fc-pregmod
  • RealAnon1800/fc-pregmod
  • brankirk/fc-pregmod
  • Amygdalan/fc-pregmod
  • DankWolf/fc-pregmod
  • Supot1951/fc-pregmod
  • bcy603/fc-pregmod
  • pwfxjpuv/fc-pregmod
  • ThreadAnon/fc-pregmod
  • Anon1800/fc-pregmod
  • Echoart/fc-pregmod
  • Dingotush/fc-pregmod
  • anonman/fc-pregmod
  • Arkerthan/fc-pregmod
  • svornost/fc-pregmod
  • wierdwierdos/fc-pregmod
  • wetwareAnon/fc-pregmod
  • QuartzHat/fc-pregmod
  • IchthysdeKilt/fc-pregmod
  • AnonAnonFC/fc-pregmod
  • Alexsis/fc-pregmod
  • LoyalTreeWP/fc-pregmod
  • aerialace/fc-pregmod
  • NurseryAnon/fc-pregmod
  • drakeashordcataclysm/fc-pregmod
  • AshVaris/fc-pregmod
  • purely0nothing/fc-pregmod
  • alex2011/fc-pregmod
  • Lindontree/fc-pregmod
  • FCaa/fc-pregmod
  • TR-8R/fc-pregmod
  • Jones/fc-pregmod
  • brr99/fc-pregmod
  • WriteAnon101/fc-pregmod
  • Drosil/fc-pregmod
  • Bob1221/fc-pregmod
  • vas/fc-pregmod
  • gitgud.user.937/fc-pregmod
  • D-K/fc-pregmod
  • AnonDev/fc-pregmod
  • madman23456/fc-pregmod
  • InarusLynx/fc-pregmod
  • Sonofrevvan/fc-pregmod
  • Randoisrando/fc-pregmod
  • cheez94/fc-pregmod
  • dldldl/fc-pregmod
  • alice321/fc-pregmod
  • Alexei91/fc-pregmod
  • darkcy/fc-pregmod
  • MapleMAD/fc-pregmod
  • pillarofsalt/fc-pregmod
  • vultureangels/fc-pregmod
  • kernel/fc-pregmod
  • nooneman/fc-pregmod
  • deepmurk/fc-pregmod
  • uglybead/fc-pregmod
  • lemongrab/fc-pregmod
  • temperence-chan/fc-pregmod
  • hcommenter/fc-pregmod
  • SpedeMemerson/fc-pregmod
  • qwijqwsf/fc-pregmod
  • BuDClow/fc-pregmod
  • HiveBro/fc-pregmod
  • shoku/fc-pregmod
  • ezsh/fc-pregmod
  • Blank/fc-pregmod
  • randoralcissian/fc-pregmod
  • benito92/fc-pregmod
  • balakart/fc-pregmod
  • wedonotsaw/fc-pregmod
  • Cayleth/fc-pregmod
  • Khip/fc-pregmod
  • Zfair/fc-pregmod
  • promethium/fc-pregmod
  • scyne/fc-pregmod
  • ZZC/fc-pregmod
  • SilverJanine/fc-pregmod
  • joxosix654email-9.co/fc-pregmod
  • Littlefootlittleguy/fc-pregmod
  • FelipeBA/fc-pregmod
  • bigtiddygothbf/fc-pregmod
  • Qotsafan/fc-pregmod
  • Zachpocalypse/fc-pregmod
  • milkanon66/fc-pregmod
  • GreGGoZZ/fc-pregmod
  • drsnarf86/fc-pregmod
  • valen102938/fc-pregmod
  • pregspammer/fc-pregmod
  • ponderin94/fc-pregmod
  • nook/fc-pregmod
  • carnifex34/fc-pregmod-mod-carni
  • SyntheticHigh/fc-pregmod
  • bob112211/fc-pregmod
  • amomynous0/fc-pregmod
  • oxone/fc-pregmod
  • MaxEuwe/fc-pregmod
  • nekoanon/fc-pregmod
  • preglocke/fc-pregmod
  • valen10293847/fc-pregmod
  • 2hu4u/fc-pregmod
  • mayibrad/fc-pregmod
  • Screm/fc-pregmod
  • Ansopedi/fc-pregmod
  • mrchaosbones/fc-pregmod
  • putrid/fc-pregmod
  • Kinnerman/fc-pregmod
  • gungrave1155/fc-pregmod
  • prndev/fc-pregmod
  • weresmilodon/fc-pregmod
  • auxxigobin/fc-pregmod
  • alice-chan/fc-pregmod
  • wigglie/fc-pregmod
  • jrliltfgb/fc-pregmod
  • Lord.alek.shade/fc-pregmod
  • truetailthesquire/fc-pregmod
  • lowercasedonkey/fc-pregmod
  • alice-chan9/fc-pregmod
  • eroglyphics/fc-pregmod
  • taliyent/fc-pregmod
  • zenzombie90/fc-pregmod
  • kjarik/fc-pregmod
  • wriggler/fc-pregmod
  • midnightblue/fc-pregmod
  • faraen/fc-pregmod
  • sigurd.cole/fc-pregmod
  • FCbuganon/fc-pregmod
  • kidkinster/fc-pregmod
  • Kar_Dragon/fc-pregmod
  • Zhafier/fc-pregmod
  • crcaretti/fc-pregmod
  • anond/fc-pregmod
  • tempmania/fc-pregmod
  • Dhanze/fc-pregmod
  • EstaUnCachucha/fc-pregmod
  • oniAnon/fc-pregmod
  • plebian/fc-pregmod
  • maxd569/fc-pregmod
  • Levarn/fc-pregmod
  • pumpkinspice/fc-pregmod
  • GammaXai/fc-pregmod
  • DanBackslide/fc-pregmod
  • i107760/fc-pregmod
  • Absimiliard/fc-pregmod
  • AmbrosiaCheesecake/fc-pregmod
  • fuguer/fc-pregmod
  • Azurel/fc-pregmod
  • Fake_Dev/fc-pregmod
  • ddongsanda/fc-pregmod
  • Combine456/fc-pregmod
  • UnwrappedGodiva/fc-pregmod
  • toyRuberDucky/fc-pregmod
  • zmobie/fc-pregmod
  • chuongk/fc-pregmod
  • BigWalnuts/fc-pregmod
  • Birdstrike/fc-pregmod
  • r3d/fc-pregmod
  • mawspa/fc-pregmod
  • sushila/fc-pregmod
  • DeathShip/fc-pregmod
  • eggrollsandwich/fc-pregmod
  • krayken/fc-pregmod
  • Reman/fc-pregmod
  • dwiafgts/fc-pregmod
  • jort93/fc-pregmod
  • teruterubouzu/fc-pregmod
  • flut/fc-pregmod
  • john-normal/fc-pregmod
  • Jonathan2405/fc-pregmod
  • Tyrgalon/fc-pregmod
  • NovX/fc-pregmod
  • Star1/fc-pregmod
  • Transhumanist01/fc-pregmod
  • m1017242/fc-pregmod
  • Rizal98798/fc-pregmod
  • jamezu369/fc-pregmod
  • thisisawittyname/fc-pregmod
  • KnightBoulegard/fc-pregmod
  • jblack/fc-pregmod
  • Souldrainr/fc-pregmod
  • torbjornhub/fc-pregmod
  • turnop/fc-pregmod
  • breadedpigeon/fc-pregmod
  • fire.maker/fc-pregmod
  • Inahaze/fc-pregmod
  • Waerjak/fc-pregmod
  • Trashman1138/fc-pregmod
  • supanintendo/fc-pregmod
  • _no0neman/fc-pregmod
  • Weslo/fc-pregmod
  • qw89/fc-pregmod
  • EvilDruid/fc-pregmod
  • dt25/fc-pregmod
  • Raou/fc-pregmod
  • DDouFu/fc-pregmod
  • Mauno/fc-pregmod
  • PandemoniumPenguin/fc-pregmod
  • AngelPuppet/fc-pregmod
  • DasUser79/fc-pregmod
  • Keaeag3s/fc-pregmod
  • HazeHazeHaze/fc-pregmod
  • hpotato/fc-pregmod
  • owouchthatbloodyhurt/fc-pregmod
  • v7Silent/fc-pregmod
  • nickylass/fc-pregmod
  • ThePrimer/fc-pregmod
  • PineCone/fc-pregmod
  • bruhmomentum17/fc-pregmod
  • CheatDude/fc-pregmod
  • synnove/fc-pregmod
  • en_bees/fc-pregmod
  • seronis/fc-pregmod
  • Nepidinepnep/fc-pregmod
  • Titanninja/fc-pregmod
  • Elohiem/fc-pregmod
  • cocoajazz/fc-pregmod
  • tfwncagf/fc-pregmod
  • ChunkyMonke/fc-pregmod
  • Dracoman671/fc-pregmod
  • jgl/fc-pregmod
  • Inev/fc-pregmod
  • jbige/fc-pregmod
  • MonsterMate/fc-pregmod
  • Konstantin6961/fc-pregmod
  • darth_ashi/fc-pregmod
  • shinx/fc-pregmod
  • Anu/fc-pregmod
  • Greytide/fc-pregmod
  • Bonafidemetal/fc-pregmod
  • Peje/fc-pregmod
  • Hexfy98/fc-pregmod
  • TooSlow/fc-pregmod
  • SoGu/fc-pregmod
  • CloudyCoffee/fc-pregmod
  • Welptard/fc-pregmod
  • Ploc/fc-pregmod-ploc
  • rain-/fc-pregmod
  • Pecanus/fc-pregmod
  • Jhortrax/fc-pregmod
  • valleytwo/fc-pregmod
  • QCmd/fc-pregmod
  • kung-wada/fc-pregmod
  • LolGaye/fc-pregmod
  • Exspiravit1/fc-pregmod
  • jadeddog/fc-pregmod
  • buster-scruggs/fs-antebellum-revivalism
  • policia123/fc-pregmod
  • evrgentesee/fc-pregmod
  • rko127/fc-pregmod
  • ExcalGrip12/fc-pregmod
  • BlackAion/fc-pregmod
  • Boss2020/fc-pregmod
  • Lawled/fc-pregmod
  • shiro/fc-pregmod
  • Skavenkeri/fc-pregmod
  • PooPooDooDooHead/fc-pregmod
  • Dugee/fc-pregmod
  • Portal124/fc-pregmod-vore
  • Fekenol/fc-pregmod
  • elGuapo/fc-pregmod
  • KelioSteel/fc-pregmod
  • sldlddk/fc-pregmod
  • lumepanter/fc-pregmod
  • ryuhana/fc-pregmod
  • Nene1009yb/fc-pregmod
  • DontAskDontTell/fc-pregmod-extra-events
  • Dulgi/fc-pregmod
  • Jate/fc-pregmod
  • percy365/fc-pregmod
  • franklygeorge/fc-pregmod
  • Dragneel117/fc-pregmod
  • vl96/fc-pregmod
  • Gorlom/fc-economicmod
  • NotAlive/fc-pregmod
  • Heretek/fc-pregmod
  • joeshmo828282/fc-pregmod
  • deswes/fc-pregmod
  • Nanana21/fc-pregmod
  • Gbr6/fc-pregmod
  • RandomNecro/fc-pregmod
  • Trinidad/fc-pregmod
  • anonymousey/fc-pregmod
  • macaronideath/fc-pregmod
  • fcbleh/fc-pregmod
  • jk3000/fc-pregmod
  • Akane/fc-pregmod
  • TheBoi/fc-pregmod
  • Sheenariel/fc-pregmod
  • Metapod/multi-custom
  • Banyanael/fc-pregmod
  • frogge/fc-pregmod
  • idkkk12385/fc-pregmod
  • Mirarara/fc-pregmod
  • DeaDa/fc-pregmod-thedeal
  • CobraCommander/fc-pregmod
  • bicobus/fc-pregmod
  • CardcaptorRLH85/fc-pregmod
  • temp-ui-start/fc-pregmod
  • PresidentConvert/fc-pregmod
  • delizious/fc-pregmod
  • Ducati/fc-pregmod
  • DerangedLoner/fc-pregmod-development-fork
  • ProjectVictory/fc-pregmod
  • forecastle/fc-pregmod
  • Apathy/fc-pregmod
  • indf/fc-pregmod-dev
  • GavAndAlt/fc-pregmod
  • hagamablabla/fc-pregmod
  • Alaco/fc-pregmod
  • DCoded/fc-pregmod
  • LittlePlague/fc-pregmod
  • MissOnahole/fc-pregmod
  • ishy2317/fc-pregmod
  • nielkazama/fc-pregmod
  • Phobos/fc-pregmod
  • kraster/fc-pregmod
  • JasWS/fc-pregmod
  • FelixJS/fc-pregmod
  • NCherfaoui/fc-pregmod
  • MidnightMoose/fc-pregmod
  • jjjjjj/fc-pregmod
  • Cl0ver/fc-pregmod
  • Pythoniqus/fc-pregmod
  • JohnMolotov/fc-pregmod
  • anonymouspregmodder/fc-pregmod-anonymouspregmodder
  • Fanatey/fc-pregmod
  • Mizako/fc-pregmod
  • Nithhogg/fc-pregmod
  • Bluecoffee/fc-pregmod
380 results
Show changes
Showing
with 33704 additions and 220 deletions
#include "fchost_storage_js.h"
#include <memory>
// static storage objects (not threadsafe, probably doesn't matter, since they'll execute within a JS context)
static std::unique_ptr<FCHostPersistentStorage> persist;
static std::unique_ptr<FCHostSessionStorage> session;
// storage handler js hook implementations
#define REGJSFUNC(x) { \
CefRefPtr<CefV8Value> func = CefV8Value::CreateFunction(x, handler); \
object->SetValue(x, func, V8_PROPERTY_ATTRIBUTE_NONE); }
static void AttachStorageFunctions(CefRefPtr<CefV8Value> object, CefRefPtr<CefV8Handler> handler)
{
REGJSFUNC("size");
REGJSFUNC("keys");
REGJSFUNC("has");
REGJSFUNC("get");
REGJSFUNC("set");
REGJSFUNC("remove");
REGJSFUNC("clear");
}
void FCHostStorageRegister(const std::filesystem::path& persistPath, CefRefPtr<CefV8Value> object) {
// New context, reset everything (don't try to run more than one app at once!)
if (persist) persist.reset(NULL);
if (session) session.reset(NULL);
persist = std::make_unique<FCHostPersistentStorage>(persistPath);
session = std::make_unique<FCHostSessionStorage>();
CefRefPtr<CefV8Handler> sess_handler = new FCHostStorageHandler(false);
CefRefPtr<CefV8Value> obj_sess = CefV8Value::CreateObject(nullptr, nullptr);
object->SetValue("FCHostSession", obj_sess, V8_PROPERTY_ATTRIBUTE_NONE);
AttachStorageFunctions(obj_sess, sess_handler);
CefRefPtr<CefV8Handler> pers_handler = new FCHostStorageHandler(true);
CefRefPtr<CefV8Value> obj_pers = CefV8Value::CreateObject(nullptr, nullptr);
object->SetValue("FCHostPersistent", obj_pers, V8_PROPERTY_ATTRIBUTE_NONE);
AttachStorageFunctions(obj_pers, pers_handler);
}
// individual execution objects for session/persistent storage
bool FCHostStorageHandler::Execute(const CefString& name, CefRefPtr<CefV8Value> object, const CefV8ValueList& arguments, CefRefPtr<CefV8Value>& retval, CefString& exception)
{
// alias pointer only, no ownership
FCHostSessionStorage* storage = persistent ? persist.get() : session.get();
if (name == "size") {
// no arguments
retval = CefV8Value::CreateInt(static_cast<int32_t>(storage->size()));
return true;
}
else if (name == "keys") {
// no arguments
retval = storage->keys();
return true;
}
else if (name == "has") {
// one string argument
if (arguments.size() < 1 || !arguments[0]->IsString()) return false;
retval = CefV8Value::CreateBool(storage->has(arguments[0]->GetStringValue()));
return true;
}
else if (name == "get") {
// one string argument
if (arguments.size() < 1 || !arguments[0]->IsString()) return false;
retval = storage->get(arguments[0]->GetStringValue());
return true;
}
else if (name == "set") {
// two arguments - one string, one "whatever"
if (arguments.size() < 2 || !arguments[0]->IsString()) return false;
storage->set(arguments[0]->GetStringValue(), arguments[1]);
retval = CefV8Value::CreateBool(true);
return true;
}
else if (name == "remove") {
// one string argument
if (arguments.size() < 1 || !arguments[0]->IsString()) return false;
retval = CefV8Value::CreateBool(storage->remove(arguments[0]->GetStringValue()));
return true;
}
else if (name == "clear") {
// no arguments
storage->clear();
return true;
}
return false;
}
#pragma once
#include "fchost_storage.h"
// storage handlers that can be registered directly to Javascript to enable fchost_storage backing
class FCHostStorageHandler : public CefV8Handler
{
public:
FCHostStorageHandler(bool _persistent) : persistent(_persistent) {};
virtual bool Execute(const CefString& name,
CefRefPtr<CefV8Value> object,
const CefV8ValueList& arguments,
CefRefPtr<CefV8Value>& retval,
CefString& exception) override;
private:
bool persistent;
IMPLEMENT_REFCOUNTING(FCHostStorageHandler);
};
void FCHostStorageRegister(const std::filesystem::path& persistPath, CefRefPtr<CefV8Value> object);
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
#include <windows.h>
#include <KnownFolders.h>
#include <ShlObj.h>
#include "fchost_app.h"
std::filesystem::path FCHostApp::GetLocalStorePath()
{
PWSTR ppath;
SHGetKnownFolderPath(FOLDERID_Documents, 0, NULL, &ppath);
std::filesystem::path local_storage = ppath;
CoTaskMemFree(ppath);
return local_storage / L"FreeCities_Pregmod";
}
// Entry point function for all processes.
int APIENTRY wWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow) {
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// Provide CEF with command-line arguments.
CefMainArgs main_args(hInstance);
// It will create the first browser instance in OnContextInitialized() after
// CEF has initialized.
CefRefPtr<FCHostApp> app(new FCHostApp);
// CEF applications have multiple sub-processes (render, plugin, GPU, etc)
// that share the same executable. This function checks the command-line and,
// if this is a sub-process, executes the appropriate logic.
int exit_code = CefExecuteProcess(main_args, app, nullptr);
if (exit_code >= 0) {
// The sub-process has completed so return here.
return exit_code;
}
CefRefPtr<CefCommandLine> command_line = CefCommandLine::CreateCommandLine();
command_line->InitFromString(lpCmdLine);
return app->main(main_args, command_line.get());
}
// Copyright (c) 2013 The Chromium Embedded Framework Authors. All rights
// reserved. Use of this source code is governed by a BSD-style license that
// can be found in the LICENSE file.
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by fchost.rc
//
#define IDI_FCHOST 100
// Avoid files associated with MacOS
#define _X86_
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NO_MFC 1
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 32700
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 102
#endif
#endif
#include "./utility.h"
CefString cef_string_from_path(const std::filesystem::path& p)
{
return CefString(p.native());
}
void cef_string_from_path(const std::filesystem::path& p, cef_string_t* str)
{
const auto& pstr = p.native();
#if defined(OS_WIN)
cef_string_from_wide(pstr.c_str(), pstr.size(), str);
#else
cef_string_from_utf8(pstr.c_str(), pstr.size(), str);
#endif
}
#pragma once
#include "include/capi/cef_base_capi.h"
#include <filesystem>
CefString cef_string_from_path(const std::filesystem::path& p);
void cef_string_from_path(const std::filesystem::path& p, cef_string_t* str);
# The workflow for updating the SugarCube format.js file
sugarcube:
echo "Compiling SugarCube"
(cd submodules/sugarcube-2/ && node build.js -n -b 2)
mv submodules/sugarcube-2/build/twine2/sugarcube-2/format.js devTools/tweeGo/storyFormats/sugarcube-2/format.js
(cd submodules/sugarcube-2/ && git diff master fc-rebased > sugarcube-fc-changes.patch)
mv submodules/sugarcube-2/sugarcube-fc-changes.patch devNotes/"sugarcube stuff"/sugarcube-fc-changes.patch
.PHONY: sugarcube
<!-- cSpell:ignore nwjs -->
# Free Cities - pregmod
Pregmod is a modification of the original [Free Cities](https://freecitiesblog.blogspot.com/) created by FCdev.
## Play the game
* WARNING - FCHost users need to update or rebuild FCHost before running alpha.34 or later (or the latest build) or it WILL DELETE YOUR LOCAL SAVES!!! Download info is at https://gitgud.io/pregmodfan/fc-pregmod/-/blob/pregmod-master/FCHost/README.md?ref_type=heads#fchost and build info is at https://gitgud.io/pregmodfan/fc-pregmod/-/blob/pregmod-master/FCHost/HowToBuild.md#how-to-build
1. Download the game
* [Current release](https://gitgud.io/pregmodfan/fc-pregmod/-/releases)
* [Latest build](https://gitgud.io/pregmodfan/fc-pregmod/-/jobs/artifacts/pregmod-master/download?job=build)
2. Open the game in your preferred browser
* On PC, we recommend either Firefox or [FCHost](FCHost/README.md).
* Recommendation: Drag it into incognito mode
3. Have fun!
### Compile the game yourself
If you want to tweak the game a bit, you can easily download the files and compile it yourself.
1. Clone the git repository:
1. [Install Git for terminal](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) or a Git GUI of your
choice.
2. Clone the repo
* Via terminal: `git clone --single-branch https://gitgud.io/pregmodfan/fc-pregmod.git`
3. Get updates
* Via terminal: `git pull`
2. Compile the game:
* Using one of two methods
1. The simple compiler by running `simple-compiler.bat` (Windows) or `simple-compiler.sh` (Mac/Linux)
* Benefits:
* Requires no external dependencies
* Slightly faster compiling
2. The advanced compiler by running `compile.bat` (Windows) or `compile.sh` (Mac/Linux)
* Requires:
* [Git](https://git-scm.com/)
* [Node.js](https://nodejs.org)
* ~500 MB of Node packages
* `compile.[bat, sh]` will attempt to help you with the installation of its dependencies
* Benefits:
* Easier debugging
* Early problem detection
* Spell checking
* Tweaking of compiler settings by running `setup.bat` (Windows) or `setup.sh` (Mac/Linux)
* Copies `FC_pregmod.html` to `FCHost` if it is installed
* Live reloading of FC after file changes by running `watcher.bat` (Windows) or `watcher.sh` (Mac/Linux)
* We suggest using the advanced compiler when possible.
* **The second run of the compiler will overwrite the existing `FC_pregmod.html` file!**
* **All our tooling expects that you are running them in FC's root directory** (Where you see devTools, src, js, etc). Failure to do so will result in errors.
* If you are using an ARM-based device, you may need to use `arch -x86_64 bash [compile or simple-compiler].sh` to properly compile.
3. To play open `FC_pregmod.html` in the `bin/` folder
* Repeat steps 2 and 3 after you make any changes or use `watcher.[bat, sh]` to do them automatically.
## Common problems
* If compiling takes a long time (more than 2 minutes) or causes a noticeable increase in system resource utilisation.
- FC's compiler makes a lot of file changes over a short period of time. Some Antivirus programs will make FC's compiler wait while it scans the contents of each changed file. So it might be worth making sure FC's directory is excluded in your Antivirus settings.
* If your Antivirus is Windows Defender (currently tested with Windows 10 on 04/14/2024):
* `Start menu` -> `Windows Security` -> `Virus & threat protection` -> `Virus & threat protection settings` ->
`Manage settings` -> `Exclusions (near the bottom)` -> `Add or remove exclusions` -> `Add an exclusion` ->
`path to FC's root directory (Where you see devTools, src, js, etc).`
* `sessionStorage quota exceeded` / `localStorage quota exceeded` or something similar
- Your saves stored inside the browser are getting too large. There are multiple ways to solve this:
1. Delete saves stored in the browser. If you want to keep them, save them to disk first.
2. Disable autosave and delete the current one. Due to technical reasons autosaves are larger than normal saves, so this may help more than expected.
3. If on Firefox, raise the storage limit: Type `about:config` in the address bar and search for
`dom.storage.default_quota`. Increase this value as needed. Default value is 5120 kilobytes / 5 MB.
4. Switch to a different browser. Recommended is either Firefox or [FCHost](FCHost/README.md), a custom HTML renderer specifically for Pregmod.
5. If you absolutely need to use Google Chrome:
1. download and unzip [NW.js SDK](https://nwjs.io/downloads/) for your operative system.
2. copy the game file (FC_pregmod.html) into the `nwjs-sdk-v0.XX.Y-YOUR_OS` folder
3. in the same folder, create a text file with the following content:
```
{
"name": "Free Cities pregmod edition",
"main": "FC_pregmod.html",
"dom_storage_quota":30
}
```
and save it as package.json. In this example, 30 is the limit (in MB) that is set for the storage quota,
but you can replace it with any number. Google Chrome has the same default value as Firefox.
4. Double click nw.exe to launch the game.
* Everything is broken!
- **Do not copy over your existing download** as it may leave old files behind, replace it entirely
* I can't save more than once or twice.
- Known issue caused by SugarCube level changes. Save to file doesn't have this problem and will likely avoid the
first problem as well.
- It is possible to increase the memory utilized by your browser to delay this
* I wish to report an issue.
1. Search [issues](https://gitgud.io/pregmodfan/fc-pregmod/-/issues) to see if someone has already reported the issue.
2. [Open a new issue](https://gitgud.io/pregmodfan/fc-pregmod/-/issues/new) or, if you are interested in trying to fix it yourself, please see our guide on [contributing](CONTRIBUTING.md).
## Contribute
New Contributors are always welcome. Basic information before you start can be found [here](CONTRIBUTING.md).
## Submodules
FC uses a modified version of SugarCube 2. More information can be found [here](devNotes/sugarcube stuff/building SugarCube.md).
further development:
-specialized slave schools
-fortifications
-more levels for militia edict (further militarize society)
-conquering other arcologies?
Events:
-famous criminal escapes to the arcology, followed by another arcology police force
Bugs:
-sometimes troop counts breaks
-sometimes rebel numbers have fractionary parts
\ No newline at end of file
<!-- cSpell:ignore faraen, eart, lxml, XMLID -->
# How to vector art
Please read the whole document before starting to work.
......@@ -7,73 +9,117 @@ Note: This does not actually describe how to be an artist.
## TL;DR
killall inkscape
artTools/vector_layer_split.py artTools/vector_source.svg tw src/art/vector/layers/
compile
killall inkscape
artTools/vector_layer_split.py artTools/vector_source.svg tw src/art/vector/layers/
compile
python3 artTools/normalize_svg.py artTools/vector_source.svg
git commit -a
## 1. Be an artist
Make changes to the vector_source.svg.
Inkscape was thoroughly tested.
Adobe Illustrator might work decently, too.
Make changes to the vector_source.svg.
Inkscape was thoroughly tested.
Adobe Illustrator might work decently, too.
## 2. Respect the structure
While editing, keep the Layers in mind.
While editing, keep the Layers in mind.
* In Inkscape, Layers are special "Inkscape groups".
* In Illustrator, Layers are groups with user-definded IDs.
* In Inkscape, Layers are special "Inkscape groups".
* In Illustrator, Layers are groups with user-defined IDs.
* All Layers should have an ID that is globally unique
(not just within their subtree).
* Please use anonymous groups only for the ease of editing. Remove all "helper" groups before finally saving the file.
* Anonymous groups can be used for continous scaling (of e.g. boobs).
* Anonymous groups can be used for continuous scaling (of e.g. boobs).
* Every asset that should go into a separate file needs to be in a labelled group
* Every asset that should go into a separate file needs to be in a labelled group
(even if that is a group with only one shape).
* There are some globally available styles defined as CSS classes (e.g. skin, hair).
Use them if your asset should be changed upon display.
Use them if your asset should be changed upon display.
Do not set the style directly in the object.
## 3. Normalise the document (before committing)
* You can use Inkscape to define and add classes to paths.
To add a class to a path, open the XML editor with the path selected (Edit → XML Editor...)
Delete all fields except for those with the names 'id', and 'd'.
Add a new field to the path and name it 'class'.
Make its value one of the values defined in the below section.
**MAINTAIN THESE CLASS DEFINITIONS BY MANUALLY EDITING YOUR WORKING SVG FILE**
<style
type="text/css"
id="style">
/* please maintain these definitions manually */
.white{fill:#FFFFFF;}
.skin{fill:#F6E0E8;}
.head{}
.eart{}
.torso{}
.tail{}
.boob{}
.penis{}
.scrotum{}
.areola{fill:#D76B93;}
.bellybutton{fill:#D76B93;}
.labia{fill:#D76B93;}
.hair{fill:#3F403F;}
.eyebrow_hair{fill:#3F403F;}
.shoe{fill:#3E65B0;}
.shoe_shadow{fill:#15406D;}
.smart_piercing{fill:#4DB748;}
.steel_piercing{fill:#787878;}
.steel_chastity{fill:#BABABA;}
.gag{fill:#BF2126;}
.shadow{fill:#010101;}
.glasses{}
.eye{}
.sclera{fill:#FFFFFF;}
.lips{}
</style>
You should be able to find this structure somewhere in the first 500 lines of your working svg file.
The formatting of these definitions matters for `normalize_svg.py`, which is invoked as part of `vector_layer_split.py` and explained below.
## 3. Normalize the document (before committing)
**THIS IS IMPORTANT**
The various editors out there (Inkscape, Illustrator) may use different variants of formatting the SVG XML.
Use
python3 normalize_svg.py vector_source.svg
python3 normalize_svg.py vector_source.svg
before committing to normalise the format so git will not freak out about the changed indentation.
before committing to normalize the format so git will not freak out about the changed indentation.
If you use Inkscape, please use in Edit → Settings → Input/Output → SVG-Output → Path Data → Optimized. This is the default.
If you use Inkscape, please use Edit → Settings → Input/Output → SVG-Output → Path Data → Optimized. This is the default.
In case your Editor uses another path data style which cannot be changed, please contact the other artists and developers via the issue tracker to find a new common ground.
What it does:
* Formats the SVG XML according to Pythons lxml module, regardless of editor.
* Adobe Illustrator uses group IDs as layer labels.
* Adobe Illustrator uses group IDs as layer labels.
Inkscape however uses layer labels and a separate, auto-generated group ID.
normalize_svg.py overwrites the group ID with the Inkscape layer label
so they are synchronised with Inkscape layer labels.
normalize_svg.py overwrites the group ID with the Inkscape layer label
so they are synchronized with Inkscape layer labels.
* Inkscape copies the global style definition into the object *every time*
the object is edited. If an object references a CSS class AND at the same time
has a local style, the local style is removed
the object is edited. If an object references a CSS class AND at the same time
has a local style, the local style is removed
so global dynamic styling is possible later on.
Note: Behaviour of Adobe Illustrator is untested.
Note: Behavior of Adobe Illustrator is untested.
## 4. Split the layers
For NoX original, deepurk extensions, execute
For NoX original, deepmurk extensions, execute
python3 vector_layer_split.py vector_source_ndmain.svg tw ../src/art/vector/layers/
python3 vector_layer_split.py vector_deepmurk_primary.svg tw ../src/art/vector/layers/
For faraen revamped art (based on NoX original)
python3 vector_revamp_layer_split.py vector_revamp_source.svg tw ../src/art/vector_revamp/layers/
. This application reads all groups in `vector_source.svg`.
Each group is stored in a separate file in the target directory `/src/art/vector/layers/`.
. This application reads all groups in `vector_source.svg`.
Each group is stored in a separate file in the target directory `/src/art/vector/layers/`.
The group ID sets the file name. Therefore, the group ID **must not** contain spaces or any other weird characters.
Also consider:
......@@ -83,14 +129,14 @@ Also consider:
* The target directory is not emptied. If a file is no longer needed, you should remove it manually.
* This procedure removes global definitions. This means, SVG filters are currently not supported.
Available output formats are `svg` and `tw`.
`svg` output exists for debug reasons.
Available output formats are `svg` and `tw`.
`svg` output exists for debug reasons.
`tw` embeds the SVG data into Twine files, but removes the global style definitions so they can be set during display.
## 5. Edit the code
`/src/art/` contains Twine code which shows the assets in the story.
There are many helpful comments in `/src/art/artWidgets.tw`.
`/src/art/` contains Twine code which shows the assets in the story.
There are many helpful comments in `/src/art/artWidgets.tw`.
The code also generates the previously removed global style definitions on the fly and per display.
## 6. Compile the story
......
This diff is collapsed.
......@@ -3,6 +3,7 @@ AttractiveLingerie
AttractiveLingerieForAPregnantWoman
BallGown
Battledress
Blouse
BodyOil
Bunny
Chains
......
......@@ -8,10 +8,10 @@
# grep -Porh '(?<=\.clothes = )"[^"]+"' src/ | sort | uniq # searches sources for all clothes strings actually used in the game, even undocumented ones
(
(
echo 'var window = {};'
grep -v '^:' src/art/vector/Helper_Functions.tw
echo -n 'Array('
sed '/^clothes:/,/:/!d' "slave variables documentation - Pregmod.txt" | grep '"' | paste -sd,
echo ').forEach(v => {console.log(window.clothing2artSuffix(v));});'
echo ').forEach(v => {console.log(window.clothing2artSuffix(v));});'
) | nodejs | sort
#!/usr/bin/env python3
'''
"""
Application for "normalizing" SVGs
These problems are addressed:
......@@ -8,80 +8,130 @@ These problems are addressed:
* Inkscape notoriously copies class styles into the object definitions.
https://bugs.launchpad.net/inkscape/+bug/167937
* Inkscape uses labels on layers. Layers are basically named groups.
* Inkscape uses labels on layers. Layers are basically named groups.
Inkscape does not sync the group id with the layer label.
Usage Example:
python3 inkscape_svg_fixup.py vector_source.svg
'''
"""
import lxml.etree as etree
import re
import sys
import lxml.etree as etree
color_classes = {
'skin', 'head', 'torso', 'boob', 'penis', 'scrotum', 'belly', 'areola', 'bellybutton', 'labia', 'hair',
'pubic_hair', 'armpit_hair', 'eyebrow_hair', 'shoe', 'shoe_shadow', 'smart_piercing', 'steel_piercing',
'steel_chastity', 'outfit_base', 'gag', 'shadow', 'glasses', 'eye', 'sclera', 'eart', 'tail',
'white', 'skin', 'skin_highlight', 'skin_shade', 'skin_strong_highlight', 'skin_strong_shade', 'arm',
'arm_highlight', 'arm_shade', 'head', 'head_highlight', 'head_shade', 'torso', 'torso_highlight',
'torso_shade', 'boob', 'boob_highlight', 'boob_shade', 'penis', 'penis_highlight', 'penis_shade',
'scrotum', 'scrotum_highlight', 'scrotum_shade', 'belly', 'belly_highlight', 'belly_shade', 'neck',
'neck_highlight', 'neck_shade', 'legs', 'legs_highlight', 'legs_shade', 'butt', 'butt_highlight',
'butt_shade', 'feet', 'feet_highlight', 'feet_shade', 'areola', 'labia', 'hair', 'shoe_shadow',
'smart_piercing', 'steel_piercing', 'steel_chastity', 'gag', 'shadow', 'glasses', 'lips', 'eyeball',
'iris', 'highlight1', 'highlight2', 'highlight3', 'highlightStrong', 'armpit_hair', 'pubic_hair',
'muscle_tone', 'belly_details', 'shoe_primary', 'shoe_primary_highlight', 'shoe_primary_shade',
'shoe_accent', 'shoe_accent_highlight', 'shoe_accent_shade', 'top_primary', 'top_primary_highlight',
'top_primary_shade', 'top_accent', 'top_accent_highlight', 'top_accent_shade', 'bottoms_primary',
'bottoms_primary_highlight', 'bottoms_primary_shade', 'bottoms_accent', 'bottoms_accent_highlight',
'bottoms_accent_shade', 'bra_primary', 'bra_primary_highlight', 'bra_primary_shade', 'bra_accent',
'bra_accent_highlight', 'bra_accent_shade', 'bra_strap1', 'bra_strap2', 'bra_strap3', 'shirt_center1',
'shirt_center2', 'shirt_center3', 'panties_primary', 'panties_primary_highlight', 'panties_primary_shade',
'panties_accent', 'panties_accent_highlight', 'panties_accent_shade', 'stockings_primary',
'stockings_accent', 'top_primary_strong_highlight', 'top_primary_strong_shade',
'top_accent_strong_highlight', 'top_accent_strong_shade', 'bottoms_primary_strong_highlight',
'bottoms_primary_strong_shade', 'bottoms_accent_strong_highlight', 'bottoms_accent_strong_shade',
'bellymask_1', 'bellymask_2', 'bellymask_3', 'bellymask_4', 'bellymask_5', 'bellymask_6', 'bellymask_7',
'bellymask_8', 'bellymask_9', 'bellymask_normal', 'bellymask_hourglass', 'bellymask_unnatural', "feet_nails"
}
def fix(tree):
# know namespaces
ns = {
'svg' : 'http://www.w3.org/2000/svg',
'inkscape' : 'http://www.inkscape.org/namespaces/inkscape'
}
# find document global style definition
# mangle it and interpret as python dictionary
style_element = tree.find('./svg:style',namespaces=ns)
style_definitions = style_element.text
pythonic_style_definitions = '{'+style_definitions.\
replace('\t.','"').replace('{','":"').replace('}','",').\
replace('/*','#')+'}'
styles = eval(pythonic_style_definitions)
# know namespaces
ns = {
'svg': 'http://www.w3.org/2000/svg',
'inkscape': 'http://www.inkscape.org/namespaces/inkscape'
}
# find document global style definition
# mangle it and interpret as python dictionary
style_element = tree.find('./svg:style', namespaces=ns)
style_definitions = style_element.text
pythonic_style_definitions = '{' + style_definitions. \
replace('\t.', '"').replace('{', '":"').replace('}', '",'). \
replace('/*', '#') + '}'
styles = eval(pythonic_style_definitions)
# go through all SVG elements
for elem in tree.iter():
if (elem.tag == etree.QName(ns['svg'], 'g')):
# compare inkscape label with group element ID
l = elem.get(etree.QName(ns['inkscape'], 'label'))
if l:
i = elem.get('id')
if (i != l):
print("Overwriting ID %s with Label %s..."%(i, l))
elem.set('id', l)
# go through all SVG elements
for elem in tree.iter():
if elem.tag == etree.QName(ns['svg'], 'g'):
# compare inkscape label with group element ID
lbl = elem.get(etree.QName(ns['inkscape'], 'label'))
if lbl:
i = elem.get('id')
if i != lbl:
print("Overwriting ID %s with Label %s..." % (i, lbl))
elem.set('id', lbl)
# clean styles (for easier manual merging)
style_string = elem.get('style')
if style_string:
split_styles = style_string.strip('; ').split(';')
styles_pairs = [s.strip('; ').split(':') for s in split_styles]
filtered_pairs = [(k, v) for k, v in styles_pairs if not (
k.startswith('font-') or
k.startswith('text-') or
k.endswith('-spacing') or
k in ["line-height", " direction", " writing", " baseline-shift", " white-space", " writing-mode"]
)]
split_styles = [':'.join(p) for p in filtered_pairs]
style_string = ';'.join(sorted(split_styles))
elem.attrib["style"] = style_string
# clean styles (for easier manual merging)
style_string = elem.get('style')
if style_string:
split_styles = style_string.strip('; ').split(';')
styles_pairs = [s.strip('; ').split(':') for s in split_styles]
filtered_pairs = [ (k,v) for k,v in styles_pairs if not (
k.startswith('font-') or
k.startswith('text-') or
k.endswith('-spacing') or
k in ["line-height", " direction", " writing", " baseline-shift", " white-space", " writing-mode"]
)]
split_styles = [':'.join(p) for p in filtered_pairs]
style_string = ';'.join(sorted(split_styles))
elem.attrib["style"] = style_string
# remove all style attributes offending class styles
s = elem.get('style')
c = elem.get('class')
if c and s:
if 'i' in locals():
s = s.lower()
c = c.split(' ')[0] # regard main style only
classes = c.split(' ')
has_color_class = any(x in color_classes for x in classes)
if has_color_class:
s_new = re.sub('fill:#[0-9a-f]+;?', '', s)
if s != s_new:
print("Explicit fill was removed from style string ({0}) for element with ID {1} "
"because its class ({2}) controls the fill color".format(s, i, c))
s = s_new
if s == 'style=""': # the style is empty now
del elem.attrib["style"]
continue
cs = ''
if c in styles:
cs = styles[c].strip('; ').lower()
if c not in styles:
print("Object id %s references unknown style class %s." % (i, c))
else:
if cs != s.strip('; '):
print("Style %s removed from object id %s differed from class %s style %s." % (s, i, c, cs))
del elem.attrib["style"]
else:
print('--------------------------- i not defined - propably a clip-path with a style ---------------------------')
print("Element tag: ".format(elem.tag))
print("Element style: ".format(s))
print("Element class: ".format(c))
print(etree.tostring(elem, pretty_print=True))
print('--------------------------- i not defined - propably a clip-path with a style ---------------------------')
# remove explicit fill color if element class is one of the color_classes
# remove all style attributes offending class styles
s = elem.get('style')
c = elem.get('class')
if (c and s):
s = s.lower()
c = c.split(' ')[0] # regard main style only
cs = ''
if c in styles:
cs = styles[c].strip('; ').lower()
if (c not in styles):
print("Object id %s references unknown style class %s."%(i,c))
else:
if (cs != s.strip('; ')):
print("Style %s removed from object id %s differed from class %s style %s."%(s,i,c,cs))
del elem.attrib["style"]
if __name__ == "__main__":
input_file = sys.argv[1]
tree = etree.parse(input_file)
fix(tree)
# store SVG into file (input file is overwritten)
svg = etree.tostring(tree, pretty_print=True)
with open(input_file, 'wb') as f:
f.write('<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n'.encode("utf-8"))
f.write(svg)
input_file = sys.argv[1]
tree = etree.parse(input_file)
fix(tree)
# store SVG into file (input file is overwritten)
svg = etree.tostring(tree, pretty_print=True)
with open(input_file, 'wb') as f:
f.write('<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n'.encode("utf-8"))
f.write(svg)
import os
import subprocess
import sys
if os.name == "nt":
minifier = "../devTools/minify/minify_win_amd64.exe"
elif sys.platform == "darwin":
minifier = "../devTools/minify/minify_darwin_amd64"
else:
# probably linux, if not we don't have an alternative anyways.
minifier = "../devTools/minify/minify_linux_amd64"
def save(layer, prefix, output_directory, output_format, svg_data):
i = layer.get('id')
output_path = os.path.join(output_directory, "{0}{1}.svg".format(prefix, i))
if output_format == 'svg':
with open(output_path, 'wb') as f:
# Header for normal SVG (XML)
f.write(f'<?xml version="1.0" encoding="UTF-8" standalone="no"?>{os.linesep}'.encode("utf-8"))
f.write(svg_data)
elif output_format == 'tw':
# send to minifier through stdin, which then saves to file
subprocess.run([minifier, "--mime=image/svg+xml", "-o", output_path],
input=svg_data)
#!/usr/bin/env python3
'''
"""
Application for procedural content adaption
*THIS IS VERY EXPERIMENTAL*
Contains a very poor man's implementation of spline mesh warping.
......@@ -23,15 +23,16 @@ python3 vector_clothing_replicator.py infile clothing bodypart destinationfile
Usage Example:
python3 vector_clothing_replicator.py vector_source.svg Straps Boob vector_destination.svg
python3 vector_clothing_replicator.py vector_source.svg Straps Torso vector_destination.svg
'''
"""
from svg.path import parse_path
import copy
import lxml.etree as etree
import sys
import lxml.etree as etree
from svg.path import parse_path
REFERENCE_PATH_SAMPLES = 200
EMBED_REPLICATIONS = True # wether to embed all replications into the input file or output separate files
EMBED_REPLICATIONS = True # whether to embed all replications into the input file or output separate files
input_file = sys.argv[1]
clothing = sys.argv[2]
......@@ -39,121 +40,127 @@ bodypart = sys.argv[3]
output_file_embed = sys.argv[4]
# TODO: make these configurable
output_file_pattern = '%s_%s_%s.svg' #bodypart, target_id, clothing
if ('Torso' == bodypart):
xpath_shape = './svg:g[@id="Torso_"]/svg:g[@id="Torso_%s"]/svg:path[@class="skin torso"]/@d' # TODO: formulate more general, independent of style
xpath_outfit_container = '//svg:g[@id="Torso_Outfit_%s_"]'%(clothing)
xpath_outfit = '//svg:g[@id="Torso_Outfit_%s_%s"]'%(clothing,'%s')
target_ids = "Unnatural,Hourglass,Normal".split(",")
reference_id = "Hourglass"
output_file_pattern = '%s_%s_%s.svg' # bodypart, target_id, clothing
if 'Torso' == bodypart:
xpath_shape = './svg:g[@id="Torso_"]/svg:g[@id="Torso_%s"]/svg:path[@class="skin torso"]/@d' # TODO: formulate more general, independent of style
xpath_outfit_container = '//svg:g[@id="Torso_Outfit_%s_"]' % (clothing)
xpath_outfit = '//svg:g[@id="Torso_Outfit_%s_%s"]' % (clothing, '%s')
target_ids = "Unnatural,Hourglass,Normal".split(",")
reference_id = "Hourglass"
else:
raise RuntimeError("Please specify a bodypart for clothing to replicate.")
raise RuntimeError("Please specify a bodypart for clothing to replicate.")
tree = etree.parse(input_file)
ns = {'svg' : 'http://www.w3.org/2000/svg'}
ns = {'svg': 'http://www.w3.org/2000/svg'}
canvas = copy.deepcopy(tree)
for e in canvas.xpath('./svg:g',namespaces=ns)+canvas.xpath('./svg:path',namespaces=ns):
# TODO: this should be "remove all objects, preserve document properties"
e.getparent().remove(e)
for e in canvas.xpath('./svg:g', namespaces=ns) + canvas.xpath('./svg:path', namespaces=ns):
# TODO: this should be "remove all objects, preserve document properties"
e.getparent().remove(e)
def get_points(xpath_shape):
'''
This funciton extracts reference paths by the given xpath selector.
Each path is used to sample a fixed number of points.
'''
paths_data = tree.xpath(xpath_shape,namespaces=ns)
points = []
path_length = None
for path_data in paths_data:
p = parse_path(path_data)
points += [
p.point(1.0/float(REFERENCE_PATH_SAMPLES)*i)
for i in range(REFERENCE_PATH_SAMPLES)
]
if (not points):
raise RuntimeError(
'No paths for reference points found by selector "%s".'%(xpath_shape)
)
return points
"""
This function extracts reference paths by the given xpath selector.
Each path is used to sample a fixed number of points.
"""
paths_data = tree.xpath(xpath_shape, namespaces=ns)
points = []
path_length = None
for path_data in paths_data:
p = parse_path(path_data)
points += [
p.point(1.0 / float(REFERENCE_PATH_SAMPLES) * i)
for i in range(REFERENCE_PATH_SAMPLES)
]
if not points:
raise RuntimeError(
'No paths for reference points found by selector "%s".' % xpath_shape
)
return points
def point_movement(point, reference_points, target_points):
'''
For a given point, finds the nearest point in the reference path.
Gives distance vector from the nearest reference point to the
respective target reference point.
'''
distances = [abs(point-reference_point) for reference_point in reference_points]
min_ref_dist_idx = min(enumerate(distances), key=lambda x:x[1])[0]
movement = target_points[min_ref_dist_idx] - reference_points[min_ref_dist_idx]
return movement
reference_points = get_points(xpath_shape%(reference_id))
container = tree.xpath(xpath_outfit_container,namespaces=ns)
if (len(container) != 1):
raise RuntimeError('Outfit container selector "%s" does not yield exactly one layer.'%(xpath_outfit_container))
"""
For a given point, finds the nearest point in the reference path.
Gives distance vector from the nearest reference point to the
respective target reference point.
"""
distances = [abs(point - reference_point) for reference_point in reference_points]
min_ref_dist_idx = min(enumerate(distances), key=lambda x: x[1])[0]
movement = target_points[min_ref_dist_idx] - reference_points[min_ref_dist_idx]
return movement
reference_points = get_points(xpath_shape % reference_id)
container = tree.xpath(xpath_outfit_container, namespaces=ns)
if len(container) != 1:
raise RuntimeError('Outfit container selector "%s" does not yield exactly one layer.' % xpath_outfit_container)
container = container[0]
outfit_source = container.xpath(xpath_outfit%(reference_id),namespaces=ns)
if (len(outfit_source) != 1):
raise RuntimeError('Outfit source selector "%s" does not yield exactly one outfit layer in container selected by "%s".'%(xpath_outfit%(reference_id), xpath_outfit_container))
outfit_source = container.xpath(xpath_outfit % reference_id, namespaces=ns)
if len(outfit_source) != 1:
raise RuntimeError(
'Outfit source selector "%s" does not yield exactly one outfit layer in container selected by "%s".' % (
xpath_outfit % reference_id, xpath_outfit_container))
outfit_source = outfit_source[0]
for target_id in target_ids:
print(
'Generating variant "%s" of clothing "%s" for bodypart "%s"...'%
(target_id, clothing, bodypart)
)
outfit = copy.deepcopy(outfit_source)
paths = outfit.xpath('./svg:path',namespaces=ns)
if target_id == reference_id:
print("This is the source variant. Skipping...")
else:
layerid = outfit.get('id').replace('_%s'%(reference_id),'_%s'%(target_id))
outfit.set('id', layerid)
outfit.set(etree.QName('http://www.inkscape.org/namespaces/inkscape', 'label'), layerid) # for the Inkscape-users
target_points = get_points(xpath_shape%(target_id))
if (len(reference_points) != len(target_points)):
raise RuntimeError(
('Different amounts of sampled points in reference "%s" and target "%s" paths. '+
'Selector "%s" probably matches different number of paths in the two layers.')%
(reference_id, target_id, xpath_shape)
)
for path in paths:
path_data = path.get("d")
p = parse_path(path_data)
for segment in p:
original_distance = abs(segment.end-segment.start)
start_movement = point_movement(segment.start, reference_points, target_points)
segment.start += start_movement
end_movement = point_movement(segment.end, reference_points, target_points)
segment.end += end_movement
distance = abs(segment.end-segment.start)
try:
# enhance position of CubicBezier control points
# amplification is relative to the distance gained by movement
segment.control1 += start_movement
segment.control1 += (segment.control1-segment.start)*(distance/original_distance-1.0)
segment.control2 += end_movement
segment.control2 += (segment.control2-segment.end)*(distance/original_distance-1.0)
except AttributeError as ae:
# segment is not a CubicBezier
pass
path.set("d", p.d())
if EMBED_REPLICATIONS:
container.append(outfit)
if not EMBED_REPLICATIONS:
container = copy.deepcopy(canvas).xpath('.',namespaces=ns)[0]
container.append(outfit)
if not EMBED_REPLICATIONS:
svg = etree.tostring(container, pretty_print=True)
with open((output_file_pattern%(bodypart, target_id, clothing)).lower(), 'wb') as f:
f.write('<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n'.encode("utf-8"))
f.write(svg)
print(
'Generating variant "%s" of clothing "%s" for bodypart "%s"...' %
(target_id, clothing, bodypart)
)
outfit = copy.deepcopy(outfit_source)
paths = outfit.xpath('./svg:path', namespaces=ns)
if target_id == reference_id:
print("This is the source variant. Skipping...")
else:
layerid = outfit.get('id').replace('_%s' % reference_id, '_%s' % target_id)
outfit.set('id', layerid)
outfit.set(etree.QName('http://www.inkscape.org/namespaces/inkscape', 'label'),
layerid) # for the Inkscape-users
target_points = get_points(xpath_shape % target_id)
if len(reference_points) != len(target_points):
raise RuntimeError(
('Different amounts of sampled points in reference "%s" and target "%s" paths. ' +
'Selector "%s" probably matches different number of paths in the two layers.') %
(reference_id, target_id, xpath_shape)
)
for path in paths:
path_data = path.get("d")
p = parse_path(path_data)
for segment in p:
original_distance = abs(segment.end - segment.start)
start_movement = point_movement(segment.start, reference_points, target_points)
segment.start += start_movement
end_movement = point_movement(segment.end, reference_points, target_points)
segment.end += end_movement
distance = abs(segment.end - segment.start)
try:
# enhance position of CubicBezier control points
# amplification is relative to the distance gained by movement
segment.control1 += start_movement
segment.control1 += (segment.control1 - segment.start) * (distance / original_distance - 1.0)
segment.control2 += end_movement
segment.control2 += (segment.control2 - segment.end) * (distance / original_distance - 1.0)
except AttributeError as ae:
# segment is not a CubicBezier
pass
path.set("d", p.d())
if EMBED_REPLICATIONS:
container.append(outfit)
if not EMBED_REPLICATIONS:
container = copy.deepcopy(canvas).xpath('.', namespaces=ns)[0]
container.append(outfit)
if not EMBED_REPLICATIONS:
svg = etree.tostring(container, pretty_print=True)
with open((output_file_pattern % (bodypart, target_id, clothing)).lower(), 'wb') as f:
f.write('<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n'.encode("utf-8"))
f.write(svg)
if EMBED_REPLICATIONS:
svg = etree.tostring(tree, pretty_print=True)
with open(output_file_embed, 'wb') as f:
f.write('<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n'.encode("utf-8"))
f.write(svg)
svg = etree.tostring(tree, pretty_print=True)
with open(output_file_embed, 'wb') as f:
f.write('<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n'.encode("utf-8"))
f.write(svg)
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.